{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Cirq Workshop Bootcamp Final",
      "provenance": [],
      "collapsed_sections": [
        "KnA4uBkwEw5-"
      ],
      "toc_visible": true,
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/JackHidary/quantumcomputingbook/blob/master/Notes/Cirq_Workshop_Bootcamp_Final.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8m9ye4AS6dE4",
        "colab_type": "text"
      },
      "source": [
        "![Cirq](https://github.com/quantumlib/cirq/raw/master/docs/Cirq_logo_color.svg?sanitize=true)\n",
        "\n",
        "\n",
        "[Cirq](https://github.com/quantumlib/cirq) is a framework for writing quantum algorithms for noisy intermediate scale quantum (NISQ) devices. Roughly speaking, NISQ devices are those with O(100) qubits that can enact O(1000) gates.  Because the resources for NISQ devices are so constrained we believe that a framework for writing programs on these devices needs to be aware of all of the architectural properties of the device on which the algorithm is written. This is in contrast to other frameworks where there is a clean separation between the abstract model being used and the details of the device.  \n",
        "\n",
        "In this tutorial we will teach you the basics of writing quantum alogorithms in Cirq. Our goal will be to write a variational ansatz for use in an optimization algorithm.\n",
        "\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "P2OBc27Elddh",
        "colab_type": "toc"
      },
      "source": [
        ">>[Installing Cirq](#scrollTo=rPgPbry6-mF3)\n",
        "\n",
        ">>[Qubits, Moments, Operations, and Circuits](#scrollTo=8A7a3jcql1l5)\n",
        "\n",
        ">>>[Create a Circuit](#scrollTo=VFwmWPf7D057)\n",
        "\n",
        ">>>[Building Circuits](#scrollTo=uaDb6B_jPgrb)\n",
        "\n",
        ">>>[Exercise: Create a circuit](#scrollTo=y9conKPAPn26)\n",
        "\n",
        ">>>>[Solution](#scrollTo=KnA4uBkwEw5-)\n",
        "\n",
        ">>[Simulations of a Circuit](#scrollTo=X15yPl_KQ20Z)\n",
        "\n",
        ">>>[Repetitions](#scrollTo=YLpiz0aN1Jd6)\n",
        "\n",
        ">>[The Deutsch-Jozsa Algorithm](#scrollTo=qFsytBIbOVD8)\n",
        "\n",
        ">>>[Exercise: Two Bit Deutsch-Jozsa Algorithm](#scrollTo=v4ka34Kuj4K0)\n",
        "\n",
        ">>>>[Solution](#scrollTo=L6vfs97CFZB2)\n",
        "\n",
        ">>[Gates](#scrollTo=mmhbPIhT4YAI)\n",
        "\n",
        ">>>[Custom Gate Implementation](#scrollTo=RACz4Z5GDMAf)\n",
        "\n",
        ">>>[Exercise: Custom Controlled Rx gate](#scrollTo=y63aDja8R4rc)\n",
        "\n",
        ">>>>[Solution](#scrollTo=7SoneHQRGeUW)\n",
        "\n",
        ">>>[Gate decompositions](#scrollTo=9W_Vlgpzz0D1)\n",
        "\n",
        ">>[Parameterized Circuits](#scrollTo=3HtlMxa6QpVo)\n",
        "\n",
        ">>>[Exercise: Rotate a qubit](#scrollTo=r-CjbPwkRI_I)\n",
        "\n",
        ">>[Noise](#scrollTo=FEM73JPALREa)\n",
        "\n",
        ">>>[Monte carlo simulations](#scrollTo=3qUM-Kpi8Iy4)\n",
        "\n",
        ">>>[Adding Noise to Circuits and Simulations](#scrollTo=weWPzaPx8zly)\n",
        "\n",
        ">>[Devices](#scrollTo=PvJCA3e0QsuI)\n",
        "\n",
        ">>>[Exercise: Make a Device](#scrollTo=lyFcloaARXg4)\n",
        "\n",
        ">>[Compiling / Optimizing](#scrollTo=J9ia4eatUQ_x)\n",
        "\n",
        ">>>[Exercise: Simplify flipped CNOTs](#scrollTo=--aUfkiaUb3S)\n",
        "\n",
        ">>[Google's Xmon Gates](#scrollTo=gM1ZW7TstvK9)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rPgPbry6-mF3",
        "colab_type": "text"
      },
      "source": [
        "---\n",
        "## Installing Cirq\n",
        "\n",
        "To use Cirq one first needs to install Cirq.  Installation instructions are available [at cirq.readthedocs.io under 'Installing Cirq'](https://cirq.readthedocs.io/en/latest/install.html).  For the purpose of using this notebook, you can run pip install to install the latest release of Cirq.  Different notebook execution systems exist but for most they have \"run\" button on a cell, which you can push, or <shift>-<enter> is often the shortcut to run th cell.  Do so in the following cell should install Cirq. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RlJBDvNgC00H",
        "colab_type": "code",
        "outputId": "f2036956-b88f-4c69-d415-5f86ce5ff76c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "# install latest version\n",
        "!pip install cirq==0.5 --quiet\n",
        "\n",
        "# Alternatively, install directly from HEAD on github:\n",
        "# !pip install git+https://github.com/quantumlib/Cirq.git --quiet"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "\u001b[K     |████████████████████████████████| 716kB 2.9MB/s \n",
            "\u001b[K     |████████████████████████████████| 12.8MB 42.8MB/s \n",
            "\u001b[31mERROR: albumentations 0.1.12 has requirement imgaug<0.2.7,>=0.2.5, but you'll have imgaug 0.2.9 which is incompatible.\u001b[0m\n",
            "\u001b[?25h"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GPjUqrK8DJTq",
        "colab_type": "text"
      },
      "source": [
        "(Note: you may see an error about `albumentations` requiring an old `imgaug`. You can ignore this error.)\n",
        "\n",
        "Let's check that Cirq has been successfully installed by importing Cirq and priting out a diagram of the Google's Bristlecone device. ![Google's Bristecone chip](https://4.bp.blogspot.com/-b9akad6ismU/WpmyaJo-cYI/AAAAAAAACa8/mCqPBJxv5oUivy6Jq42FSOQYkeRlTmkiwCLcBGAs/s1600/image1.png)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FTrmLyq4C2gf",
        "colab_type": "code",
        "outputId": "e8ef722e-1c29-4fdb-dadb-458d7ca12bff",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 544
        }
      },
      "source": [
        "import cirq\n",
        "import numpy as np\n",
        "import matplotlib \n",
        "print(cirq.google.Bristlecone)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "                                             (0, 5)────(0, 6)\n",
            "                                             │         │\n",
            "                                             │         │\n",
            "                                    (1, 4)───(1, 5)────(1, 6)────(1, 7)\n",
            "                                    │        │         │         │\n",
            "                                    │        │         │         │\n",
            "                           (2, 3)───(2, 4)───(2, 5)────(2, 6)────(2, 7)───(2, 8)\n",
            "                           │        │        │         │         │        │\n",
            "                           │        │        │         │         │        │\n",
            "                  (3, 2)───(3, 3)───(3, 4)───(3, 5)────(3, 6)────(3, 7)───(3, 8)───(3, 9)\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  │        │        │        │         │         │        │        │\n",
            "         (4, 1)───(4, 2)───(4, 3)───(4, 4)───(4, 5)────(4, 6)────(4, 7)───(4, 8)───(4, 9)───(4, 10)\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "(5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4)───(5, 5)────(5, 6)────(5, 7)───(5, 8)───(5, 9)───(5, 10)───(5, 11)\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5)────(6, 6)────(6, 7)───(6, 8)───(6, 9)───(6, 10)\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  (7, 2)───(7, 3)───(7, 4)───(7, 5)────(7, 6)────(7, 7)───(7, 8)───(7, 9)\n",
            "                           │        │        │         │         │        │\n",
            "                           │        │        │         │         │        │\n",
            "                           (8, 3)───(8, 4)───(8, 5)────(8, 6)────(8, 7)───(8, 8)\n",
            "                                    │        │         │         │\n",
            "                                    │        │         │         │\n",
            "                                    (9, 4)───(9, 5)────(9, 6)────(9, 7)\n",
            "                                             │         │\n",
            "                                             │         │\n",
            "                                             (10, 5)───(10, 6)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "09zRgohCMiBs",
        "colab_type": "text"
      },
      "source": [
        "The import ran without raising an error, and the output is in fact the grid of qubits for the Bristlecone device. Looks like the install worked!\n",
        "\n",
        "Be aware that Cirq is still alpha software, meaning **we are still making breaking changes all the time**. If you don't want your project to suddenly go from working to not working when we release a new version, you should depend on a *specific version* of Cirq and periodically bump that version to the latest one. For the purposes of this tutorial, we will use version of `0.5` (i.e. `cirq==0.5` in pip's version notation).\n",
        "\n",
        "---"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8A7a3jcql1l5",
        "colab_type": "text"
      },
      "source": [
        "## Qubits, Moments, Operations, and Circuits\n",
        " \n",
        " In Cirq, circuits are represented either by a `Circuit` object or a `Schedule` object. `Schedule`s offer more control over quantum gates and circuits at the timing level.\n",
        "\n",
        "Conceptually: a `Circuit` is a collection of `Moment`s. A `Moment` is a collection of `Operation`s that all act during the same abstract time slice. An `Operation` is a an effect that operates on a specific subset of Qubits. The most common type of `Operation` is a `Gate` applied to several qubits (a \"`GateOperation`\"). The following diagram should help illustrate these concepts.\n",
        "\n",
        "![Circuits, Moments, and Operations.](https://cirq.readthedocs.io/en/latest/_images/CircuitMomentOperation.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VFwmWPf7D057",
        "colab_type": "text"
      },
      "source": [
        "### Create a Circuit\n",
        "\n",
        "Let's create a `Circuit`.  Note that in the previous cell we imported cirq, so we will assume that cirq has been imported through out the rest of this notebook."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "pE88WsFeDGfs",
        "colab_type": "code",
        "outputId": "1cd2f7ca-19a6-46d6-87c3-b082ff88ffed",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        }
      },
      "source": [
        "a = cirq.NamedQubit(\"a\")\n",
        "b = cirq.NamedQubit(\"b\")\n",
        "c = cirq.NamedQubit(\"c\")\n",
        "ops = [cirq.H(a), cirq.H(b), cirq.CNOT(b, c), cirq.H(b)]\n",
        "circuit = cirq.Circuit.from_ops(ops)\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───H───────────\n",
            "\n",
            "b: ───H───@───H───\n",
            "          │\n",
            "c: ───────X───────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-06jQwEdI4DJ",
        "colab_type": "text"
      },
      "source": [
        "We can unpack this a bit and see all of the components for the circuit.\n",
        "\n",
        "\n",
        "The first thing we do is pick some qubits to use. There are many different types of qubits in Cirq, and you can define your own by inheriting from the `cirq.Qid` class. There's nothing inherently special or magical about these quantum id types such as `cirq.NamedQubit`. They simply identify what you wish to operate on, which is relevant when you are targeting a specific device. For example, if we were creating a circuit for the Bristlecone device we would use `cirq.GridQubit(5, 0)` to refer to the qubit in the left most position of the device. To keep these simple for now, we'll start with abstract qubits simply identified by a name such as \"a\".\n",
        "```\n",
        "a = cirq.NamedQubit(\"a\")\n",
        "```\n",
        "\n",
        "Next we encounter of the object `cirq.H`, which is a Hadamard gate.  `cirq.H` is an instance of the `cirq.HGate` class, which itself is a subclass of `Gate` (along with other classes). \n",
        "$$H = {1 \\over \\sqrt{2}} \\left[ \\begin{array}[cc]  & 1 & 1  \\\\ 1 & -1 \\end{array}\\right]$$\n",
        "We can use cirq to see this unitary matrix:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YKfg575v1DQB",
        "colab_type": "code",
        "outputId": "c4545dfe-b172-4d2f-af37-5bb866325c98",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "cirq.unitary(cirq.H)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array([[ 0.70710678+0.j,  0.70710678+0.j],\n",
              "       [ 0.70710678+0.j, -0.70710678+0.j]])"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 5
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hJMAciW21KEg",
        "colab_type": "text"
      },
      "source": [
        "`Gate` objects have the ability to applied \"on\" to one or more qubits.  There are two ways to do this for gates, either using the `on` method or by directly calling the gate on the qubits as if the gate were a function and the qubits were arguments.  For example to apply the `H` onto qubit `a` we can say\n",
        "```\n",
        "cirq.H.on(a)\n",
        "```\n",
        "or \n",
        "```\n",
        "cirq.H(a)\n",
        "```\n",
        "\n",
        "The result of those expressions is a `GateOperation` object, which is a type of `Operation`.\n",
        "\n",
        "In cirq we make a strong distinction between `Operation`s and `Gate`s. An `Operation` is associated with specific qubits and can be put in `Circuit`s. A `Gate` has unspecified qubits, and will produce an operation when given qubits.\n",
        "\n",
        "Once you have a collection of operations, you can construct a `Circuit` using the class method `Circuit.from_ops` (more on that in a minute):\n",
        "```\n",
        "circuit = cirq.Circuit.from_ops(ops)\n",
        "```\n",
        "The last thing we did in the example code was use the (surprisingly useful) ability to print the circuit as a text diagram.\n",
        "\n",
        "The diagram is visually helpful, but it doesn't really get into the internal details of how the `Circuit` is represented. \n",
        "A `Circuit` is made up of a sequence of `Moment` objects.\n",
        "And each `Moment` object is a list of non-overlapping `Operation`s.\n",
        "To see this internal structure, we can iterate over the `Moment`s in the `Circuit` while printing them out."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hH-y4JiEMv25",
        "colab_type": "code",
        "outputId": "863afc8b-0ae8-488d-f714-0f554016949e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "for i, moment in enumerate(circuit):\n",
        "    print('Moment {}: {}'.format(i, moment))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Moment 0: H(a) and H(b)\n",
            "Moment 1: CNOT(b, c)\n",
            "Moment 2: H(b)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pm5iC7MNQY6-",
        "colab_type": "text"
      },
      "source": [
        "We can also just print the circuit's `repr`, which returns a somewhat more detailed (if less readable) expression."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2Y6zG_peQG1y",
        "colab_type": "code",
        "outputId": "f4a8a5f9-b5be-42f1-f50a-6077f579105c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 221
        }
      },
      "source": [
        "print(repr(circuit))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "cirq.Circuit(moments=[\n",
            "    cirq.Moment(operations=[\n",
            "        cirq.H.on(cirq.NamedQubit('a')),\n",
            "        cirq.H.on(cirq.NamedQubit('b')),\n",
            "    ]),\n",
            "    cirq.Moment(operations=[\n",
            "        cirq.CNOT.on(cirq.NamedQubit('b'), cirq.NamedQubit('c')),\n",
            "    ]),\n",
            "    cirq.Moment(operations=[\n",
            "        cirq.H.on(cirq.NamedQubit('b')),\n",
            "    ]),\n",
            "])\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zyVbU8yfW_qi",
        "colab_type": "text"
      },
      "source": [
        "The usefulness of printing the `repr` is that it includes *all* the gory details.\n",
        "These details can be useful when debugging.\n",
        "The `repr` is also a valid python expression that evaluates to the circuit.\n",
        "For example, if we notice that a circuit generated in some complicated way triggers a bug in a simulator, copy-pasting the generated circuit's `repr` into a test, and then working from there, is a simple way to decouple the reproduction of the bug from the circuit generation code."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uaDb6B_jPgrb",
        "colab_type": "text"
      },
      "source": [
        "### Building Circuits\n",
        "Above we created the `Circuit` using `from_ops`.  But there are many ways to construct and modify circuits, and each of these is useful in different contexts.  Here are a few examples:\n",
        "\n",
        "\n",
        "1.   `from_ops`: This is the simplest way to make a circuit. Give this method some operations, and out pops a circuit.\n",
        "2.  `append`:  `Circuit`s are mutable. You can start with an empty `c = cirq.Circuit()` and simply `c.append(operations)` to add on more and more operations \n",
        "3. `insert`:  Instead of appending, you can insert before a particular moment location (labeled by an integer index)\n",
        "4.  By using `Circuit`'s constructor, which takes a list of `Moment`s. Each `Moment` must be explicitly constructed with its own list of `Operation`s. This is tedious, but gives complete control over how the operations are layed out.\n",
        "\n",
        "One interesting, and extremely convenient, fact about `from_ops`, `append`, and `insert` is that they \"auto flatten\" whatever you give them.\n",
        "You *can* give them a list of operations, but you can also give them a list *of lists* of operations.\n",
        "Or a generator function that sometimes yields tuples of operations and other times yields individual operations.\n",
        "Or just a single operation (without a list around it).\n",
        "If it can recursively iterated into individual operations, `from_ops` and `append` and `insert` will take it.\n",
        "\n",
        "The main place where auto-flattening is useful is when you are generating a circuit's operations using generator functions.\n",
        "This is jumping a bit ahead of what we've explained, but basically auto-flattening means that generators producing operations for a circuit can simply `yield` sub-generators (instead of iterating over them and yielding their items):\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QFoV-eOE1tGN",
        "colab_type": "code",
        "outputId": "7eefda71-cd46-4c59-8b7b-bbd779c2b123",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 170
        }
      },
      "source": [
        "def xor_swap(a, b):\n",
        "    yield cirq.CNOT(a, b)\n",
        "    yield cirq.CNOT(b, a)\n",
        "    yield cirq.CNOT(a, b)\n",
        "  \n",
        "def left_rotate(qubits):\n",
        "    for i in range(len(qubits) - 1):\n",
        "        a, b = qubits[i:i+2]\n",
        "        yield xor_swap(a, b)\n",
        "\n",
        "line = cirq.LineQubit.range(5)\n",
        "print(cirq.Circuit.from_ops(left_rotate(line)))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0: ───@───X───@───────────────────────────────────────\n",
            "      │   │   │\n",
            "1: ───X───@───X───@───X───@───────────────────────────\n",
            "                  │   │   │\n",
            "2: ───────────────X───@───X───@───X───@───────────────\n",
            "                              │   │   │\n",
            "3: ───────────────────────────X───@───X───@───X───@───\n",
            "                                          │   │   │\n",
            "4: ───────────────────────────────────────X───@───X───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p9LUxAU41wWs",
        "colab_type": "text"
      },
      "source": [
        "You may have noticed that there is a hole in what we've explained so far.\n",
        "`from_ops` effectively takes a 1-dimensional sequence of operations, but the output is a 2-dimensional circuit (a list-of-lists-of-operations).\n",
        "There is a degree of freedom that hasn't been account for.\n",
        "Specifically: how does cirq choose the moment that each operation will be placed within?\n",
        "The answer is: it depends on the  `InsertStrategy` you choose.\n",
        "\n",
        "There are currently four insertion strategies in Cirq:\n",
        "\n",
        "1.  `InsertStrategy.EARLIEST` (currently the default)\n",
        "2. `InsertStrategy.NEW`,\n",
        "3. `InsertStrategy.INLINE`\n",
        "4.  `InsertStrategy.NEW_THEN_INLINE` \n",
        "\n",
        "`InsertStrategy.EARLIEST` is defined as\n",
        "> `InsertStrategy.EARLIEST`: Scans backward from the insert\n",
        "> location until a moment with operations touching qubits affected by the\n",
        "> operation to insert is found. The operation is added into the moment just\n",
        "> after that location.\n",
        "\n",
        "For example, if we first create an `Operation` in a single moment,\n",
        "and then use `InsertStrategy.EARLIEST` the `Operation` can slide back to this\n",
        "first `Moment` if there is space\n",
        "\n",
        "An `InsertStrategy` defines how ``Operations`` are placed in a `Circuit` when requested to be inserted at a given location.\n",
        "Here a `location` is identified by the index of the `Moment` in the `Circuit` that operations should be placed before (in the case of `Circuit.append` this means inserting at the index `len(circuit)`; which is one more than the largets moment index and so represents the end of the circuit)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wNek1WjpX4MR",
        "colab_type": "code",
        "outputId": "0f6c34e5-95e2-4266-d590-83a08b893971",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        }
      },
      "source": [
        "circuit = cirq.Circuit()\n",
        "circuit.append([cirq.CZ(a, b)])\n",
        "circuit.append([cirq.H(a), cirq.H(b), cirq.H(c)])\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───@───H───\n",
            "      │\n",
            "b: ───@───H───\n",
            "\n",
            "c: ───H───────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TcHeZM6qXvbS",
        "colab_type": "text"
      },
      "source": [
        "After creating the first moment with a `CZ` gate, the second\n",
        "append uses the `InsertStrategy.EARLIEST` strategy. The\n",
        "`H` on ``a`` and ``b`` cannot slide back, while the `H` on ``c`` can and so ends up in the first `Moment`.\n",
        "\n",
        "`InsertStrategy.EARLIEST` is the default strategy, the second most important strategy is `InsertStrategy.NEW_THEN_INLINE`:\n",
        "> `InsertStrategy.NEW_INLINE`: For the first operation, add it to a new \n",
        "> `Moment` the insertion point.  Attempts to add the operation after the first \n",
        "> operation to insert into the moment just before the desired insert location. \n",
        "> But, if there's already an existing operation affecting any of the qubits\n",
        "> touched by the operation to insert, a new moment is created instead and this \n",
        "> `Moment` is the one that is subsequently used for insertions.\n",
        "\n",
        "As an example of this examine this code"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qWVDhLxFYuRp",
        "colab_type": "code",
        "outputId": "566dcc0e-d8f6-49ea-cb2b-affc72c21353",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        }
      },
      "source": [
        "circuit = cirq.Circuit()\n",
        "circuit.append([cirq.CZ(a, b)])\n",
        "circuit.append([cirq.H(c), cirq.H(b), cirq.H(b), cirq.H(a)], )\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───@───H───────\n",
            "      │\n",
            "b: ───@───H───H───\n",
            "\n",
            "c: ───H───────────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y9conKPAPn26",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Create a circuit\n",
        "\n",
        "Now that you've learned about `InsertStrategy`, here is an exercise to validate your understanding.  Create, using the least number of appends the following circuit (note that printing a circuit in Cirq does not always print a moment by moment structure e.g. to avoid overlapping operations in the diagram, but here imagine that you want exactly the moments indicated by the spacing of the circuit.)\n",
        "\n",
        "\n",
        "\n",
        "```\n",
        "a: ───@───H───────────H───H───\n",
        "      │\n",
        "b: ───@───────H───@───H───────\n",
        "                  │\n",
        "c: ───H───────────@───────────\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KnA4uBkwEw5-",
        "colab_type": "text"
      },
      "source": [
        "#### Solution"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jP4VkPeHcjJT",
        "colab_type": "code",
        "cellView": "both",
        "outputId": "d9c2e321-c475-4107-b677-1a8aa785bc2d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 126
        }
      },
      "source": [
        "#@title\n",
        "a = cirq.NamedQubit('a')\n",
        "b = cirq.NamedQubit('b')\n",
        "c = cirq.NamedQubit('c')\n",
        "circuit = cirq.Circuit()\n",
        "circuit.append([cirq.CZ(a, b), cirq.H(c), cirq.H(a)] )\n",
        "circuit.append([cirq.H(b), cirq.CZ(b, c), cirq.H(b), cirq.H(a), cirq.H(a)],\n",
        "               strategy=cirq.InsertStrategy.NEW_THEN_INLINE)\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───@───H───────────H───H───\n",
            "      │\n",
            "b: ───@───────H───@───H───────\n",
            "                  │\n",
            "c: ───H───────────@───────────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X15yPl_KQ20Z",
        "colab_type": "text"
      },
      "source": [
        "## Simulations of a Circuit\n",
        "\n",
        "Now that you know how to construct a `Circuit` in Cirq, let's use Cirq to simulate the circuit.\n",
        "\n",
        "Here is a simple circuit"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "V6tZk3qGqBoH",
        "colab_type": "code",
        "outputId": "c334a530-4f52-40b5-c363-b22b620f217e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "def basic_circuit(measure=True):\n",
        "    sqrt_x = cirq.X**0.5\n",
        "    cz = cirq.CZ\n",
        "    yield sqrt_x(a), sqrt_x(b)\n",
        "    yield cz(a, b)\n",
        "    yield sqrt_x(a), sqrt_x(b)\n",
        "    if measure:\n",
        "        yield cirq.measure(a,b)\n",
        "        \n",
        "circuit = cirq.Circuit.from_ops(basic_circuit())\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───X^0.5───@───X^0.5───M───\n",
            "              │           │\n",
            "b: ───X^0.5───@───X^0.5───M───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WpywVOeDqi4Q",
        "colab_type": "text"
      },
      "source": [
        "There are a few things to note here.  \n",
        "\n",
        "One is that we have used a Python *generator*. Recall that in Python functions that have a `yield` are *generators*. Generators are functions that act as *iterators*. Above we see that we can iterate over ``basic_circuit()``. We see that when we do this each of the `yields` produces what was yielded, and here these are `Operations`,\n",
        "or lists of ``Operations``. But when we pass this iterator to the append method, something magical happens. `Circuit` is able to flatten all of these an pass them as one giant list to `Circuit.append` (this also works for `Circuit.insert`).\n",
        "> The above idea uses a concept we call an ``OP_TREE``. An ``OP_TREE`` is\n",
        "> not a class, but a contract. The basic idea is that, if the input can be\n",
        "> iteratively flattened into a list of operations, then the input is an\n",
        "> ``OP_TREE``.\n",
        "\n",
        "A very nice pattern emerges from this structure: define *generators* for sub-circuits, which can vary by size\n",
        "or `Operation` parameters.\n",
        "\n",
        "Now we can simulate this circuit."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "KmGuMjvGw_Ef",
        "colab_type": "code",
        "outputId": "eeb10381-43c0-47f4-a494-64d692e5cce3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "simulator = cirq.Simulator()\n",
        "circuit = cirq.Circuit.from_ops(basic_circuit())\n",
        "result = simulator.run(circuit)\n",
        "print('Measurement results')\n",
        "print(result)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Measurement results\n",
            "a,b=1, 0\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aHugx9T0z047",
        "colab_type": "text"
      },
      "source": [
        "Running this multiple times should result in different measurement results, since the above circuit produces a superposition over all computational basis states.   \n",
        "\n",
        "Above we used the `run` method on the simulator.  These methods mimic the actual hardware in that they don't give one access to unphysical objects like the wavefunction.  If one wants to get the wave function, then the `simulate` methods can do this:\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Apj7WiFZ0WFm",
        "colab_type": "code",
        "outputId": "b6ff4a3b-9864-4cd1-fa4c-125cef105cda",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "circuit = cirq.Circuit()\n",
        "circuit.append(basic_circuit(measure=False))    \n",
        "result = simulator.simulate(circuit, qubit_order=[a, b])\n",
        "\n",
        "print('Wavefunction:')\n",
        "print(np.around(result.final_state, 3))\n",
        "print('Dirac notation:')\n",
        "print(result.dirac_notation())"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Wavefunction:\n",
            "[0.5+0.j  0. +0.5j 0. +0.5j 0.5+0.j ]\n",
            "Dirac notation:\n",
            "0.5|00⟩ + 0.5j|01⟩ + 0.5j|10⟩ + 0.5|11⟩\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "t18-sIJc0cvf",
        "colab_type": "text"
      },
      "source": [
        "Notice that we passed a `qubit_orde`r into the `simulate` method.  This order helps define the order of the kronecker product used in the resulting `final_state` vector.  The `qubit_order` argument is optional. When it is omitted, qubits are sorted ascending according to the ordering methods defined by their python class (for example `cirq.NamedQubit` sorts lexicographically by name).\n",
        "If there are multiple types of qubits in one circuit, the name of the type is used as a tie breaker.\n",
        "\n",
        "The simplest `qubit_order` value you can provide is a list of the qubits in the desired ordered. Any qubits from the circuit that are not in the list will be ordered using the  default `__str__` ordering, but come after qubits that are in the list. Be aware that all qubits in the list are included in the simulation, even if they are not operated on by the circuit.\n",
        "\n",
        "The mapping from the order of the qubits to the order of the  amplitudes in the wave function can be tricky to understand.  Basically, it is the same as the ordering used by `numpy.kron`.\n",
        "\n",
        "> If wave function is array \n",
        ">>(0.1, 0.2, 0.3, 0.4)\n",
        "\n",
        "> then this is \n",
        ">> 0.1|00⟩ + 0.2|01⟩ + 0.3|10⟩ + 0.4|11⟩ \n",
        "\n",
        ">in Dirac notation.  If the \n",
        ">> qubit order = [a, b]\n",
        "\n",
        ">then |00> means qubit a is in 0 and qubit b is in 0, |01> means \n",
        "> qubit a is 0 and qubit b is 1, etc.\n",
        "\n",
        "Another way to think about the qubit-to-amplitude ordering is as \"for loop ordering\":\n",
        "\n",
        "```\n",
        "for a in [0, 1]:\n",
        "    for b in [0, 1]:\n",
        "        print(a, b)\n",
        "```\n",
        "\n",
        "The first index (the outermost loop) is the slowest to vary."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YLpiz0aN1Jd6",
        "colab_type": "text"
      },
      "source": [
        "### Repetitions\n",
        "\n",
        "The simulator `run` methods also take an option for repeating the circuit. If \n",
        "the measurements in the circuit are terminal, and all other operations are unitary, this simulator is optimized to not recompute the wavefunction before sampling from the circuit.  So for example this code doesn't recompute the wave function but knows to sample from the final measurements"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QxkmBlo21lrQ",
        "colab_type": "code",
        "outputId": "38e875c3-d785-4d60-bbb4-7be32bd6a856",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops(basic_circuit())\n",
        "result = simulator.run(circuit, repetitions=1000)\n",
        "print(result.histogram(key='a,b'))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Counter({1: 261, 2: 248, 3: 247, 0: 244})\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bD0zX0zP2HxQ",
        "colab_type": "text"
      },
      "source": [
        "Here we have also demonstrated the use of the `histogram` method on the `result` which sums over all the different results for all of the different repetitions.\n",
        "\n",
        "The `histogram` method can also be given a `fold_func` argument, in order to group measurement results under some key before counting them up.\n",
        "For example, we can group by whether or not the two measurement results agreed:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rPqVUsD9snYf",
        "colab_type": "code",
        "outputId": "a5010f7b-1e40-4413-f612-a771b36fb985",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "print(result.histogram(key='a,b', fold_func=lambda e: 'agree' if e[0] == e[1] else 'disagree'))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Counter({'disagree': 509, 'agree': 491})\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qFsytBIbOVD8",
        "colab_type": "text"
      },
      "source": [
        "## The Deutsch-Jozsa Algorithm\n",
        "\n",
        "The very first indication that quantum computers could be more powerful than classical computers was provided by David Deutsch in his 1985 paper:\n",
        "> David Deutsch,  \"Quantum Theory, the Church-Turing Principle and the Universal Quantum Computer\" Proceedings of the Royal Society of London A. 400: 97 [PDF](https://people.eecs.berkeley.edu/~christos/classics/Deutsch_quantum_theory.pdf)\n",
        "\n",
        "This algorithm was extended by Deutsch and Richard Jozsa to a more convincing algorithmic seperation and what is now called the Deutsch-Jozsa algorithm.  In this section we will show how to write circuits for the Deutsch algorithm and then as an exercise in using Cirq for algorithms for a small version of the Deutsch-Jozsa algorithm.\n",
        "\n",
        "Let's begin with the Deutsch algorithm.  In Deutsch's algorithm you are given access to a box which computes a one bit boolean function.  That is it is a box which takes in a bit and outputs a bit.  If we want to be a mathematician or theoretical computer scientist we write the function $f$ as $f: \\{0, 1\\} \\rightarrow \\{0, 1\\}$.  There are exactly four such boolean functions which we can write out in a table\n",
        "\n",
        "| $x$ | $f_0$ | $f_1$ | $f_x$ | $f_{\\bar{x}}$ |\n",
        "| --- |  --- | --- | --- | --- |\n",
        "| 0 | 0 | 1 | 0 | 1\n",
        "| 1 | 0 | 1 | 1 | 0\n",
        "\n",
        "The first two of these are *constant* functions, $f_0$ and $f_1$.  That is they always output a constant value.  The other two $f_x$ and $f_\\bar{x}$ are *balanced*.  Over their inputs $0$ and $1$, they have an equal number of $0$s and $1$s in their truth table.  \n",
        "\n",
        "We can now state Deutsch's problem:\n",
        "\n",
        "> Given access to a one bit input one bit output boolean function, determine, by querying the function whether the function is *balanced* or *constant*.\n",
        "\n",
        "It shouldn't take you much to convince yourself that in order to solve this problem classically you need to call the function on both possible input values.  The easy way to see this is just to consider what happens if you query the function on one particular input and notice that for either input learning the value of the function does not separate the constant from balanced functions.\n",
        "\n",
        "**Classically one must query the binary function twice to distinguish the constant function from the balanced function.**\n",
        "\n",
        "\n",
        "\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UAec5ZBuSWYU",
        "colab_type": "text"
      },
      "source": [
        "Now lets turn to the quantum approach to this problem.  There is one bit of book keeping we need to take care of.  Above we have described a classical function on bits that is not reversible.  That is, knowing the values of the output does not allow us to determine uniquely the value of the input.  In order to run this on a quantum computer, however we need to make this computation reversible.  A trick for taking a classical non-reversible function and making it quantum happy is to compute the value in an extra register.  Suppose we have an $n$ bit input $x$ and we are computing a (potentially non-reverisble) boolean function $f(x)$.  Then we can implement this via a Unitary $U_f$ that acts like on $n + 1$ qubits\n",
        "\n",
        "$$U_f |x\\rangle |y\\rangle = |x\\rangle | y \\oplus f(x)\\rangle$$\n",
        "\n",
        "Here $\\oplus$ is addition modulo $2$ (XOR) and we have identified how $U_f$ acts by its action on all computational basis states $|x\\rangle$ ($n$ input qubits) and ($1$ output qubit)$|y\\rangle$. To see that this is reversible one can note that applying the transformation twice returns the state to its original form.\n",
        "\n",
        "Let's see how to implement these functions in Cirq.\n",
        "\n",
        "$f_0$ enacts the transform\n",
        "$$\n",
        "\\begin{eqnarray}\n",
        "|00\\rangle &\\rightarrow&  |00\\rangle \\\\\n",
        "|01\\rangle &\\rightarrow&  |01\\rangle \\\\\n",
        "|10\\rangle &\\rightarrow&  |10\\rangle \\\\\n",
        "|11\\rangle &\\rightarrow&  |11\\rangle \\\\\n",
        "\\end{eqnarray}\n",
        "$$\n",
        "Well this is just the identity transform, i.e. an empty circuit.\n",
        "\n",
        "$f_1$ enacts the transform\n",
        "$$\n",
        "\\begin{eqnarray}\n",
        "|00\\rangle &\\rightarrow&  |01\\rangle \\\\\n",
        "|01\\rangle &\\rightarrow&  |00\\rangle \\\\\n",
        "|10\\rangle &\\rightarrow&  |11\\rangle \\\\\n",
        "|11\\rangle &\\rightarrow&  |10\\rangle \\\\\n",
        "\\end{eqnarray}\n",
        "$$\n",
        "This is the `cirq.X` bit flip gate on the second qubit.\n",
        "\n",
        "$f_x$ enacts the transform\n",
        "$$\n",
        "\\begin{eqnarray}\n",
        "|00\\rangle &\\rightarrow&  |00\\rangle \\\\\n",
        "|01\\rangle &\\rightarrow&  |01\\rangle \\\\\n",
        "|10\\rangle &\\rightarrow&  |11\\rangle \\\\\n",
        "|11\\rangle &\\rightarrow&  |10\\rangle \\\\\n",
        "\\end{eqnarray}\n",
        "$$\n",
        "This is nothing more than a `cirq.CNOT` from the first bit to the second bit.\n",
        "\n",
        "Finally $f_\\bar{x}$ enacts the transform\n",
        "$$\n",
        "\\begin{eqnarray}\n",
        "|00\\rangle &\\rightarrow&  |01\\rangle \\\\\n",
        "|01\\rangle &\\rightarrow&  |00\\rangle \\\\\n",
        "|10\\rangle &\\rightarrow&  |10\\rangle \\\\\n",
        "|11\\rangle &\\rightarrow&  |11\\rangle \\\\\n",
        "\\end{eqnarray}\n",
        "$$\n",
        "which is a `cirq.CNOT` from the first bit to the second bit followed by a `cirq.X` on the second bit.\n",
        "\n",
        "We can encapulate these functions into a dictionary from a oracle name to the ops in the circuit needed to enact this function:\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YtWiBHonly69",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "q0, q1 = cirq.LineQubit.range(2)\n",
        "oracles = {\n",
        "    '0': [],\n",
        "    '1': [cirq.X(q1)],\n",
        "    'x': [cirq.CNOT(q0, q1)],\n",
        "    'notx': [cirq.CNOT(q0, q1), cirq.X(q1)]\n",
        "}    "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "axCSYj3EmAEo",
        "colab_type": "text"
      },
      "source": [
        "Turning now to Deutch's algorithm.  Suppose we are given access to the reversible oracle functions we have defined before.  By a similar argument for our irreversible classical functions you can show that you cannot distinguish the balanced from the constant functions by using this oracle only once.  But now we can ask the question: what if we are allowed to query this box in superposition, i.e. what if we can use the power of quantum computing?\n",
        "\n",
        "Deutsch was able to show that you could solve this problem now, with quantum computers, using only a single query of the box.  To see how this works we need two simple insights.\n",
        "\n",
        "Suppose that we prepare the second qubit in the superposition state $|-\\rangle=\\frac{1}{\\sqrt{2}}(|0\\rangle-|1\\rangle)$ and apply  the oracle.  Then we can check that\n",
        "$$ \n",
        "U_f |x\\rangle |-\\rangle = U_f|x\\rangle \\frac{1}{\\sqrt{2}}(|0\\rangle -|1\\rangle ) = |x\\rangle \\frac{1}{\\sqrt{2}}(|f(x)\\rangle -|f(x) \\oplus 1\\rangle ) =  (-1)^{f(x)} |x\\rangle |-\\rangle\n",
        "$$  \n",
        "This is the so called \"phase kickback trick\".  By applying $U_f$ onto a target which is in superposition, the value of the function ends up showing up in the global phase.  \n",
        "\n",
        "How can we leverage this to distinguish between the constant and balanced functions?  Note that for the constant functions the phase that is applied is the same for all inputs $|x\\rangle$, whereas for the balanced functions the phase is different for each value of $x$.   Another way to say that is that if we use the phase kickback trick then for each of the oracles we apply the following transform on the first qubit.\n",
        "$$\n",
        "\\begin{eqnarray}\n",
        "f_0 \\rightarrow I, &&\n",
        "f_1 \\rightarrow -I, &&\n",
        "f_x \\rightarrow Z, &&\n",
        "f_\\bar{x} \\rightarrow -Z &&\n",
        "\\end{eqnarray}\n",
        "$$\n",
        "Now we only need, on the first qubit, to distinguish between the identity gate and the $Z$ gate.  But we can do this by recalling the identity\n",
        "$$ H Z H = X$$\n",
        "where $H$ is the Hamadard gate\n",
        "$$H = {1 \\over \\sqrt{2}} \\left[ \\begin{array}[cc]  & 1 & 1  \\\\ 1 & -1 \\end{array}\\right]$$\n",
        "This means that we can turn a phase flip into a bit flip by applying Hadamards before and after the phase flip.  If we look at the constant and balanced functions we see that this means that the constant functions will be proportional to $I$ and the balanced will be proportional to $X$.  If we feed in $|0\\rangle$ to this register, then in the first cases we will only see $|0\\rangle$ and in the second case we will see $|1\\rangle$.  In other words we will be able to distinguish constant from balanced using a single query of the oracle.\n",
        "\n",
        "\n",
        "Let's code this up\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "aMHzLxztj-gq",
        "colab_type": "code",
        "outputId": "39f47a81-ec55-436c-ac73-ff56fd3dcc7d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 373
        }
      },
      "source": [
        "q0, q1 = cirq.LineQubit.range(2)\n",
        "oracles = {\n",
        "    '0': [],\n",
        "    '1': [cirq.X(q1)],\n",
        "    'x': [cirq.CNOT(q0, q1)],\n",
        "    'notx': [cirq.CNOT(q0, q1), cirq.X(q1)]\n",
        "}    \n",
        "\n",
        "def deutsch_algorithm(oracle):\n",
        "    yield cirq.X(q1)\n",
        "    yield cirq.H(q0), cirq.H(q1)\n",
        "    yield oracle\n",
        "    yield cirq.H(q0)\n",
        "    yield cirq.measure(q0)\n",
        "\n",
        "for key, oracle in oracles.items():\n",
        "    print('Circuit for {}...'.format(key))\n",
        "    print('{}\\n'.format(cirq.Circuit.from_ops(deutsch_algorithm(oracle))))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Circuit for 0...\n",
            "0: ───H───H───M───\n",
            "\n",
            "1: ───X───H───────\n",
            "\n",
            "Circuit for 1...\n",
            "0: ───H───H───M───\n",
            "\n",
            "1: ───X───H───X───\n",
            "\n",
            "Circuit for x...\n",
            "0: ───H───────@───H───M───\n",
            "              │\n",
            "1: ───X───H───X───────────\n",
            "\n",
            "Circuit for notx...\n",
            "0: ───H───────@───H───M───\n",
            "              │\n",
            "1: ───X───H───X───X───────\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Zy-2ysxzweyv",
        "colab_type": "text"
      },
      "source": [
        "Lets run these circuits a bunch of times to see that the measurement result ends up correctly distinguishing constant from balanced."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ImffrBgJvLme",
        "colab_type": "code",
        "outputId": "502de354-439b-4b76-a750-8320c2678f27",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 88
        }
      },
      "source": [
        "simulator = cirq.Simulator()\n",
        "for key, oracle in oracles.items():\n",
        "    result = simulator.run(cirq.Circuit.from_ops(deutsch_algorithm(oracle)), \n",
        "                          repetitions=10)\n",
        "    print('oracle: {:<4} results: {}'.format(key, result))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "oracle: 0    results: 0=0000000000\n",
            "oracle: 1    results: 0=0000000000\n",
            "oracle: x    results: 0=1111111111\n",
            "oracle: notx results: 0=1111111111\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v4ka34Kuj4K0",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Two Bit Deutsch-Jozsa Algorithm\n",
        "\n",
        "All boolean functions for one input bit are either constant or balanced.  For boolean functions from two input bits not all functions are constant or balanced.  There are two constant functions, $f(x_0, x_1) = 0$ and $f(x_0, x_1)=1$, while there are ${4 \\choose 2} = 6$ balanced functions.  The following code gives you the operations for these functions where we take two input qubits and compute the function in the third qubit\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "V5ZCXGCrxl4k",
        "colab_type": "code",
        "outputId": "b1d066f8-171f-46e3-aa4f-b27859ef6538",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1003
        }
      },
      "source": [
        "q0, q1, q2 = cirq.LineQubit.range(3)\n",
        "constant = ([], [cirq.X(q2)])\n",
        "balanced = ([cirq.CNOT(q0, q2)], [cirq.CNOT(q1, q2)], [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2)],\n",
        "            [cirq.CNOT(q0, q2), cirq.X(q2)], [ cirq.CNOT(q1, q2), cirq.X(q2)], [cirq.CNOT(q0, q2), cirq.CNOT(q1, q2), cirq.X(q2)])\n",
        "for i, ops in enumerate(constant):\n",
        "    print('\\nConstant function {}'.format(i))\n",
        "    print(cirq.Circuit.from_ops(ops).to_text_diagram(qubit_order=[q0, q1, q2]))\n",
        "    print()\n",
        "\n",
        "for i, ops in enumerate(balanced):\n",
        "    print('\\nBalanced function {}'.format(i))\n",
        "    print(cirq.Circuit.from_ops(ops).to_text_diagram(qubit_order=[q0, q1, q2]))\n",
        "    \n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "\n",
            "Constant function 0\n",
            "0: ───\n",
            "\n",
            "1: ───\n",
            "\n",
            "2: ───\n",
            "\n",
            "\n",
            "Constant function 1\n",
            "0: ───────\n",
            "\n",
            "1: ───────\n",
            "\n",
            "2: ───X───\n",
            "\n",
            "\n",
            "Balanced function 0\n",
            "0: ───@───\n",
            "      │\n",
            "1: ───┼───\n",
            "      │\n",
            "2: ───X───\n",
            "\n",
            "Balanced function 1\n",
            "0: ───────\n",
            "\n",
            "1: ───@───\n",
            "      │\n",
            "2: ───X───\n",
            "\n",
            "Balanced function 2\n",
            "0: ───@───────\n",
            "      │\n",
            "1: ───┼───@───\n",
            "      │   │\n",
            "2: ───X───X───\n",
            "\n",
            "Balanced function 3\n",
            "0: ───@───────\n",
            "      │\n",
            "1: ───┼───────\n",
            "      │\n",
            "2: ───X───X───\n",
            "\n",
            "Balanced function 4\n",
            "0: ───────────\n",
            "\n",
            "1: ───@───────\n",
            "      │\n",
            "2: ───X───X───\n",
            "\n",
            "Balanced function 5\n",
            "0: ───@───────────\n",
            "      │\n",
            "1: ───┼───@───────\n",
            "      │   │\n",
            "2: ───X───X───X───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mr9ltXSJyB79",
        "colab_type": "text"
      },
      "source": [
        "An extension of Deutsch's orginal algorithm is the Deutsch-Jozsa algorithm, which can distinguish constant from balanced functions like these using a single query to the oracle.  Write a quantum circuit that can distinguish these "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qJP_e68e1JBs",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "simulator = cirq.Simulator()\n",
        "def your_circuit(oracle):\n",
        "    # your code here\n",
        "    yield oracle\n",
        "    # and here\n",
        "    yield cirq.measure(q0, q1, q2)\n",
        "\n",
        "print('Your result on constant functions')\n",
        "for oracle in constant:\n",
        "    result = simulator.run(cirq.Circuit.from_ops(your_circuit(oracle)), repetitions=10)\n",
        "    print(result)\n",
        "    \n",
        "print('Your result on balanced functions')\n",
        "for oracle in balanced:\n",
        "    result = simulator.run(cirq.Circuit.from_ops(your_circuit(oracle)), repetitions=10)\n",
        "    print(result)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L6vfs97CFZB2",
        "colab_type": "text"
      },
      "source": [
        "#### Solution"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "mUvm9rmRFb4p",
        "colab_type": "code",
        "cellView": "both",
        "outputId": "e8be7f58-7ab0-4c03-d6e2-4bdbda93a055",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 195
        }
      },
      "source": [
        "#@title\n",
        "simulator = cirq.Simulator()\n",
        "def your_circuit(oracle):\n",
        "    # phase kickback trick\n",
        "    yield cirq.X(q2), cirq.H(q2)\n",
        "    \n",
        "    # equal superposition over input bits\n",
        "    yield cirq.H(q0), cirq.H(q1)\n",
        "    \n",
        "    # query the function\n",
        "    yield oracle\n",
        "    \n",
        "    # interference to get result, put last qubit into |1>\n",
        "    yield cirq.H(q0), cirq.H(q1), cirq.H(q2)\n",
        "    \n",
        "    # a final OR gate to put result in final qubit\n",
        "    yield cirq.X(q0), cirq.X(q1), cirq.CCX(q0, q1, q2)\n",
        "    yield cirq.measure(q2)\n",
        "    \n",
        "\n",
        "print('Your result on constant functions')\n",
        "for oracle in constant:\n",
        "    result = simulator.run(cirq.Circuit.from_ops(your_circuit(oracle)), repetitions=10)\n",
        "    print(result)\n",
        "    \n",
        "print('Your result on balanced functions')\n",
        "for oracle in balanced:\n",
        "    result = simulator.run(cirq.Circuit.from_ops(your_circuit(oracle)), repetitions=10)\n",
        "    print(result)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Your result on constant functions\n",
            "2=0000000000\n",
            "2=0000000000\n",
            "Your result on balanced functions\n",
            "2=1111111111\n",
            "2=1111111111\n",
            "2=1111111111\n",
            "2=1111111111\n",
            "2=1111111111\n",
            "2=1111111111\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mmhbPIhT4YAI",
        "colab_type": "text"
      },
      "source": [
        "## Gates"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F5ubTic94X79",
        "colab_type": "text"
      },
      "source": [
        "Cirq comes with a plethora of different common gates.  Here we show a few of them."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "iIpoDaqK4yjV",
        "colab_type": "code",
        "outputId": "a45d27b8-42c6-4484-afed-9bc9e42b37b5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 106
        }
      },
      "source": [
        "q0, q1, q2 = cirq.LineQubit.range(3)\n",
        "ops = [\n",
        "    cirq.X(q0),\n",
        "    cirq.Y(q1),\n",
        "    cirq.Z(q2),\n",
        "    cirq.CZ(q0,q1),\n",
        "    cirq.CNOT(q1,q2),\n",
        "    cirq.H(q0),\n",
        "    cirq.T(q1),\n",
        "    cirq.S(q2),\n",
        "    cirq.CCZ(q0, q1, q2),\n",
        "    cirq.SWAP(q0, q1),\n",
        "    cirq.CSWAP(q0, q1, q2),\n",
        "    cirq.CCX(q0, q1, q2),\n",
        "    cirq.ISWAP(q0, q1),\n",
        "    cirq.Rx(0.5 * np.pi)(q0),\n",
        "    cirq.Ry(.5 * np.pi)(q1),\n",
        "    cirq.Rz(0.5 * np.pi)(q2),\n",
        "    (cirq.X**0.5)(q0),\n",
        "]\n",
        "print(cirq.Circuit.from_ops(ops))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0: ───X───@───H───────@───×───@───@───iSwap──────Rx(0.5π)───X^0.5───\n",
            "          │           │   │   │   │   │\n",
            "1: ───Y───@───@───T───@───×───×───@───iSwap──────Ry(0.5π)───────────\n",
            "              │       │       │   │\n",
            "2: ───Z───────X───S───@───────×───X───Rz(0.5π)──────────────────────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FKRGtncT7R0v",
        "colab_type": "text"
      },
      "source": [
        "For each of these gates, you can figure out how they act on the computational basis by calling `cirq.unitary` on the gate.  For example"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "7SUAT5F17afR",
        "colab_type": "code",
        "outputId": "7aecbabc-f890-4dd0-d587-0ed0feddef7c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 88
        }
      },
      "source": [
        "print(cirq.unitary(cirq.CNOT))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[1.+0.j 0.+0.j 0.+0.j 0.+0.j]\n",
            " [0.+0.j 1.+0.j 0.+0.j 0.+0.j]\n",
            " [0.+0.j 0.+0.j 0.+0.j 1.+0.j]\n",
            " [0.+0.j 0.+0.j 1.+0.j 0.+0.j]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Me6u5EQY76KA",
        "colab_type": "code",
        "outputId": "c4023c93-df7d-434e-cf2f-69b0fec0b7e9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "print(cirq.unitary(cirq.Rx(0.5 * np.pi)))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[0.70710678+0.j         0.        -0.70710678j]\n",
            " [0.        -0.70710678j 0.70710678+0.j        ]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "flm4CcqT2DoI",
        "colab_type": "text"
      },
      "source": [
        "For single qubit gates, we have named gates like `cirq.H` for the Hadmard gate, but probably more useful are the single qubit rotation gates.\n",
        "\n",
        "$$ \n",
        "{\\tt cirq.Rx(θ)}: \\exp(-i \\theta X) = cos \\theta I - i \\sin \\theta X =\\left[ \\begin{array} ~\\cos \\theta & -i \\sin \\theta \\\\ -i \\sin \\theta & \\cos \\theta\\end{array} \\right] \\\\\n",
        "$$ \n",
        "$$ \n",
        "{\\tt cirq.Ry(θ)}: \\exp(-i \\theta Y) = cos \\theta I - i \\sin \\theta Y =\\left[ \\begin{array} ~\\cos \\theta &  -\\sin \\theta \\\\  \\sin \\theta & \\cos \\theta\\end{array} \\right] \\\\\n",
        "$$ \n",
        "$$ \n",
        "{\\tt cirq.Rz(θ)}: \\exp(-i \\theta Z) = cos \\theta I - i \\sin \\theta Z =\\left[ \\begin{array} ~e^{i \\theta} & 0 \\\\ 0 & e^{-i \\theta} \\end{array} \\right] \\\\\n",
        "$$ \n",
        "\n",
        "In addition to `cirq.unitary` another important method (behind the scenes, anyways) is `cirq.apply_unitary_to_tensor`.  This allows you to apply a unitary gate onto a state.  Of course we could have applied the unitary directly to the state, using `cirq.unitary`.   We'll see below in understanding how these methods are implemented that the `cirq.apply_unitary_to_tensor` can be used to apply the gate more directly onto the state and can save allocations of memory to store the unitary.  \n",
        "\n",
        "If we apply `cirq.Rx` to a state we can see how it rotates the state.  To do this let us introduce a new simulate method `simulate_moment_steps`.  This allows us to simulate the circuit `Moment` by `Moment`.  At each point we can access the state.  For example here we can use this to create a circuit that is a series of small `cirq.Rx` rotations and plot the probablility of measureing the state in the $|0\\rangle$ state:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "UgoNBN1H8B6h",
        "colab_type": "code",
        "outputId": "b5b30c14-9896-472d-99de-89ffc1f5d9e7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 371
        }
      },
      "source": [
        "a = cirq.NamedQubit('a')\n",
        "circuit = cirq.Circuit.from_ops([cirq.Rx(np.pi / 50.0)(a) for theta in range(200)])\n",
        "print('Circuit is a bunch of small rotations about Pauli X axis:')\n",
        "print('{}\\n'.format(circuit))\n",
        "p0 = []\n",
        "z = []\n",
        "print('We step through the circuit and plot the z component of the vector '\n",
        "      'as a function of index of the moment being stepped over.')\n",
        "for i, step in enumerate(simulator.simulate_moment_steps(circuit)):\n",
        "    prob = np.abs(step.state_vector()) ** 2\n",
        "    z.append(i)\n",
        "    p0.append(prob[0])\n",
        "matplotlib.pyplot.style.use('seaborn-whitegrid')\n",
        "matplotlib.pyplot.plot(z, p0, 'o')"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Circuit is a bunch of small rotations about Pauli X axis:\n",
            "a: ───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───Rx(0.02π)───\n",
            "\n",
            "We step through the circuit and plot the z component of the vector as a function of index of the moment being stepped over.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x7f73f4530908>]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 24
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nxZ5PP/0UTz75JFasWIGSkhJ8+eWXqn6qra3FG2+8gYSEBCxduhQPPvhg3O3auHEjBgYGkJSUhO3b\ntyM7Oxu5ubnIz88PfO63v/0tEhP1rdMezrYNGzaoat5sn61ZswYdHR0AgM7OTtx44414/PHHcc89\n9wT0lZmZiZ07dxpq18gYMXvpdYlWAAAFtklEQVT2bOM0pliQAwcOKI899piiKIpy4sQJZenSpaba\nU19frzz66KOKoijKmTNnlAULFijPPfec8v7775tq1/79+5Wnnnoq6NiGDRuUd999V1EURdmxY4ey\ne/duM0wL4sCBA0p5eblSUlKiHD9+PO7/v7u7WykpKVGef/555c0331QURd1P3d3dyh133KGcO3dO\n6e3tVe666y6lo6MjrnatX79e+fOf/6woiqK89dZbygsvvKAoiqLcfPPNhtkRqW1qmreCz4azYcMG\n5ciRI8qpU6eU++67zzA7RqIWI4zUmCWHSkJt3mAGN910E375y18CAK644gr09vZicDC6spfx4sCB\nA/j+978PALj99tstsbHFK6+8gieffNK0/z9u3Djs2rUrqHKlmp+OHDmC2bNnIz09HcnJycjPz8eh\nQ4fialdZWRkWL14MYOgusbOz07D/H61taljBZ35OnjyJ8+fPm/J0rhYjjNSYJQO3z+dDZmZm4LV/\n8wazSExMDDze7927F7feeisSExPx1ltvYfny5XjmmWdw5swZU2w7ceIEnnjiCSxbtgz79u1Db29v\nYGjkG9/4hql+A4CjR4/i6quvDpQC3rlzJx566CFs3rwZfX19cbEhKSkJycnJQcfU/OTz+UZtDGKk\n/9TsSk1NRWJiIgYHB/H73/8e99xzDwDgwoULKC0thcfjweuvv26YTaFsAzBK81bwmZ/f/e53KCkp\nCbz2+XxYs2YNPB5P0LCdEajFCCM1Ztkx7uEoFpmx+Le//Q179+7Fb37zGzQ1NSEjIwM5OTn49a9/\njZdffhmbN2+Oqz3XXXcdVq9ejR/+8Ic4deoUli9fHvQkYAW/7d27F/fddx8AYPny5ZgxYwamTp2K\nsrIy7N69GytXrjTZQm0/meW/wcFBrF+/HvPmzcP8+fMBAOvXr8e9994Lh8OBkpISzJ07F7Nnx7eU\n6o9+9KNRms/Lywt6j1k+u3DhAhoaGlBeXg4AyMjIwNNPP417770X58+fx4MPPoh58+YZvl/A8Bhx\nxx13BI7rrTFL3nGH2rzBLD788EP86le/wq5du5Ceno758+cHSlsuXLgQn376adxtmjRpEu688044\nHA5MnToVV155Jc6ePRu4k7XCxhYHDhwIXNyLFi3C1KlTAZjnMz+pqamj/KSmOzP8t3HjRlx77bVY\nvXp14NiyZcswYcIEpKamYt68eab4Tk3zVvHZxx9/HDREkpaWhvvvvx9OpxNZWVmYNWsWTp48aagN\nI2OEkRqzZOAOtXmDGZw/fx4vvvgiXn311UBG/amnngrsr3ngwIHAzI54Ultbi9deew0A0N7ejq++\n+gpLliwJ+O6vf/0rvve978XdLj+nT5/GhAkTMG7cOCiKghUrVuDcuaG9Ls3ymZ/vfve7o/x0ww03\n4NixYzh37hy6u7tx6NAhzJ07N6521dbWwul0Ys2aNYFjJ0+eRGlpKRRFwcDAAA4dOmSK79Q0bwWf\nAcCxY8eCyk3v378fW7duBQD09PTgk08+MXSvALUYYaTGLDlUorZ5g5m8++676OjowNq1awPHlixZ\ngrVr1yIlJQWpqakBkcSThQsXYt26dfj73/+O/v5+lJeXIycnB8899xyqqqowefJkFBUVxd0uP8M3\nk3Y4HFi6dClWrFiBlJQUTJo0CU899VRc7GhqasILL7wAr9eLpKQk1NXV4ec//zk2bNgQ5Cen04nS\n0lKsXLkSDocDq1atQnp6elzt+uqrrzB+/Hg8/PDDAIaS8+Xl5bjqqqvwwAMPICEhAQsXLjQ8Aadm\nW0lJySjNJycnm+6zl156Ce3t7YGnOQCYO3cuampqUFxcjMHBQTz22GOYNGmSYXapxYht27bh+eef\nN0RjXPJOCCE2w5JDJYQQQrRh4CaEEJvBwE0IITaDgZsQQmwGAzchhNgMBm5CCLEZDNyEEGIzGLgJ\nIcRm/H+kb0IZ9EzN/AAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6q0YkXSZBvzd",
        "colab_type": "text"
      },
      "source": [
        "Above we have given ourselves direct access to the wave function and calculated the exact probabilities.  Suppose we wanted to sample from the wave function at each point instead.  "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "iynhJEvoCIro",
        "colab_type": "code",
        "outputId": "0a58d7c0-4873-4ff1-c78f-dbfa1027010b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 280
        }
      },
      "source": [
        "repetitions = 100\n",
        "a = cirq.NamedQubit('a')\n",
        "circuit = cirq.Circuit.from_ops([cirq.Rx(np.pi / 50.0)(a) for theta in range(200)])\n",
        "p0 = []\n",
        "z = []\n",
        "for i, step in enumerate(simulator.simulate_moment_steps(circuit)):\n",
        "    samples = step.sample([a], repetitions=repetitions)\n",
        "    prob0 = np.sum(samples, axis=0)[0] / repetitions\n",
        "    p0.append(prob0)\n",
        "    z.append(i)\n",
        "matplotlib.pyplot.style.use('seaborn-whitegrid')\n",
        "matplotlib.pyplot.plot(z, p0, 'o')"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x7f73f455b7b8>]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 25
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nCYIgTMb/B2FSOblmSllpAAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RACz4Z5GDMAf",
        "colab_type": "text"
      },
      "source": [
        "### Custom Gate Implementation\n",
        "\n",
        "Supose there is a gate that you want Cirq to support, but it is not implemented in Cirq.  How do you go about adding a new gate?  \n",
        "\n",
        "Cirq tries to be Pythonic.  One way in which it does this is that it relies on Python's **protocol** pattern.  Protocols are similar to interfaces, in that they define a collection of methods that an object must support to implement a protocol, but different in that this requirement is more informal and not a part of a class or interface declaration.  An object supports a protocol if it implements the methods that the protocol defines.  You're probably familiar with this if you've ever done something like defined your own `Container` in Python.  To do this for an object you simply define the `__contains__`, `__setitem__`, and `__getitem__` methods on your object, and then you can use this object anywere the Container protocol is supported.  \n",
        "\n",
        "Let's see how this works for defining a custom gate.  The gate we will define is a single qubit gate that has only rational amplitudes. This is based on the famous 3, 4, 5 triangle you may remember from a long ago math class: $3^2 + 4^2 = 5^2$.  Using this observation we can construct normalized vectors and a unitary transform using on ratios of $3$, $4$, and $5$: \n",
        "$$\n",
        "\\zeta =\\left[ \\begin{array}\n",
        "~\\frac{3}{5} & \\frac{4}{5} \\\\\n",
        "-\\frac{4}{5} & \\frac{3}{5}\n",
        "\\end{array} \\right]\n",
        "$$\n",
        "\n",
        "Here is a simple implementation of this gate.  To do this we simply define a class that inherits from `cirq.SingleQubitGate` and implements the `cirq.SupportsUnitary` protocol by implementing the `_unitary_(self)` method.  We also define a `__str__` representation and by default Cirq will use this gate when priting out the gate in a circuit diagram.\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Y2a7t2qmLDTb",
        "colab_type": "code",
        "outputId": "ce7d52f8-5d8a-44b5-8546-bedba496030d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "class RationalGate(cirq.SingleQubitGate):\n",
        "    \n",
        "    def _unitary_(self):\n",
        "        return np.array([[3 / 5, 4 / 5], [-4 / 5, 3 / 5]])\n",
        "    \n",
        "    def __str__(self):\n",
        "        return 'ζ'\n",
        "\n",
        "\n",
        "a = cirq.NamedQubit('a')\n",
        "rg = RationalGate()\n",
        "print(cirq.Circuit.from_ops([rg(a)]))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───ζ───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "x9dHKNfgMoyz",
        "colab_type": "code",
        "outputId": "a7383e94-5d29-4bb4-8490-33c1d6d7d81f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "print(cirq.unitary(rg))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[ 0.6  0.8]\n",
            " [-0.8  0.6]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xmF-uscmPP_G",
        "colab_type": "text"
      },
      "source": [
        "Let's check that we can use this gate in a simulation."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_RXBrSQ8PWnu",
        "colab_type": "code",
        "outputId": "658eeb46-a7d9-43d6-a188-543d70114825",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops([rg(a)])\n",
        "simulator = cirq.Simulator()\n",
        "result = simulator.simulate(circuit)\n",
        "print(result.final_state)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[ 0.6+0.j -0.8+0.j]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TY4zT46KQcjS",
        "colab_type": "text"
      },
      "source": [
        "Implemeting `_unitary_` allows cirq to call `cirq.unitary` on an object.\n",
        "\n",
        "There is actually an interesting advanced subtly here, which is that many methods in cirq that ostensibly depend on a gate's unitary try to avoid the `_unitary_` method, because it is extremely inefficient for gates over many qubits.\n",
        "In most cases the method `_apply_unitary_to_tensor_` will be used instead, if it is available.\n",
        "This method allows much more fine grained control on how a unitary is applied to a state, but it is harder to implemented as it is expected to e.g. use the pre-allocated workspace buffer that was given to it.\n",
        "Almost all of the basic gates we have defined in Cirq have this method implemented.\n",
        "TL;DR if you need to get performant multiqubit gates you should implement a custom `_apply_unitary_to_tensor_`."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y63aDja8R4rc",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Custom Controlled Rx gate\n",
        "\n",
        "Recall that the `cirq.Rx` gate is a rotation about the $X$ pauli axis:\n",
        "$$ \n",
        "{\\tt cirq.Rx(θ)}: \\exp(-i \\theta X) = cos \\theta I - i \\sin \\theta X =\\left[ \\begin{array} ~\\cos \\theta & -i \\sin \\theta \\\\ -i \\sin \\theta & \\cos \\theta\\end{array} \\right] \\\\\n",
        "$$ \n",
        "\n",
        "As an exercise, create a two qubit controlled `cirq.Rx` gate:\n",
        "$$\n",
        "{\\tt CRx(\\theta)}:\n",
        "\\left[\\begin{array}\n",
        "~1 & 0 & 0 & 0 \\\\\n",
        "0 & 1 & 0 & 0  \\\\\n",
        "0 & 0 & \\cos \\theta & -i \\sin \\theta \\\\\n",
        "0 & 0 & -i \\sin \\theta & \\cos \\theta\n",
        "\\end{array} \\right]\n",
        "$$"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9htgTzqAYHsA",
        "colab_type": "code",
        "outputId": "30a8c2a9-e00a-4fe8-dd83-c3f26c741d1f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "class CRx(cirq.TwoQubitGate):\n",
        "\n",
        "    def __init__(self, theta):\n",
        "        self.theta = theta\n",
        "\n",
        "    def _unitary_(self):\n",
        "      return np.array([\n",
        "        \n",
        "      ])\n",
        "    \n",
        "\n",
        "    \n",
        "    pass\n",
        "\n",
        "    # Get this to print nicely in an ASCII circuit, you should also\n",
        "    # implement the _circuit_diagram_info_ method from the\n",
        "    # SupportsCircuitDiagramInfo protocol. You can return a tuple\n",
        "    # of strings from this method.\n",
        "\n",
        "\n",
        "print(np.around(cirq.unitary(CRx(0.25 * np.pi))))     "
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zVsfg8erYhiw",
        "colab_type": "code",
        "outputId": "9500fb4e-767a-419c-8866-aa0235c07cdd",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "# Also get your class to print a circuit correctly.\n",
        "\n",
        "a = cirq.NamedQubit('a')\n",
        "b = cirq.NamedQubit('b')\n",
        "op = CRx(0.25 * np.pi)(a, b)\n",
        "print(cirq.Circuit.from_ops([op]))\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───<__main__.CRx object at 0x7f49e3ad3fd0>───\n",
            "      │\n",
            "b: ───#2────────────────────────────────────────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7SoneHQRGeUW",
        "colab_type": "text"
      },
      "source": [
        "#### Solution"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XaG8n5bdGgf2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class CRx(cirq.TwoQubitGate):\n",
        "\n",
        "    def __init__(self, theta):\n",
        "        self.theta = theta\n",
        "\n",
        "    def _unitary_(self):\n",
        "      return np.array([\n",
        "          [1, 0, 0, 0],\n",
        "          [0, 1, 0, 0],\n",
        "          [0, 0, np.cos(self.theta), -1j * np.sin(self.theta)],\n",
        "          [0, 0, -1j * np.sin(self.theta), np.cos(self.theta)]\n",
        "      ])\n",
        "    \n",
        "    def _circuit_diagram_info_(self, args):\n",
        "        return '@', 'Rx({}π)'.format(self.theta / np.pi)\n",
        "    \n",
        "\n",
        "print('cirq.unitary on the gate yields:')\n",
        "print(np.around(cirq.unitary(CRx(0.25 * np.pi))))\n",
        "print()\n",
        "\n",
        "a = cirq.NamedQubit('a')\n",
        "b = cirq.NamedQubit('b')\n",
        "op = CRx(0.25 * np.pi)(a, b)\n",
        "print('Circuit diagram:')\n",
        "print(cirq.Circuit.from_ops([op]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9W_Vlgpzz0D1",
        "colab_type": "text"
      },
      "source": [
        "### Gate decompositions"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Uun4giev4N8F",
        "colab_type": "text"
      },
      "source": [
        "In many context, the notion of what gate you can apply is defined either by the physical hardware you are running or perhaps on the quantum error correcting code you are working with.  In quantum computing we typically talk about **gate sets** and work with respect to a given gate set.  Cirq supports gate decomposition via the `decompose` protocol.  Below we show how one can construct a gate that can be decomposed into two gates."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9G-9_29h09Mx",
        "colab_type": "code",
        "outputId": "f79d5980-cc02-4269-d98b-da2359e4201c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "class HXGate(cirq.SingleQubitGate):\n",
        "    \n",
        "    def _decompose_(self, qubits):\n",
        "        return cirq.H(*qubits), cirq.X(*qubits)\n",
        "    \n",
        "    def __str__(self):\n",
        "        return 'HX'\n",
        "    \n",
        "HX = HXGate()\n",
        "\n",
        "a = cirq.NamedQubit('a')\n",
        "circuit = cirq.Circuit.from_ops([HX(a)])\n",
        "print(circuit)\n",
        "print(cirq.Circuit.from_ops(cirq.decompose(circuit)))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───HX───\n",
            "a: ───Y^0.5───X───X───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RLF1narS66iB",
        "colab_type": "text"
      },
      "source": [
        "Note that this not only decomposed the `HX` gate into `H` and `X`, it also decomposed `H` into `Y**0.5` and `X`.  In order to decompose only once, one can use `cirq.decompose_once`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AS-YMmAv6zUg",
        "colab_type": "code",
        "outputId": "258b3f2b-a847-4bfb-d021-e1c51aa94efb",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "print(cirq.Circuit.from_ops(cirq.decompose_once(HX(a))))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───H───X───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sQ5N0UR26G_v",
        "colab_type": "text"
      },
      "source": [
        "When we define a gate, it is good practice to give a default decompose in terms of gates in `common_gates`.  However often you will want to change this decomposition at run time for the specific hardware or context you are working in.  To do this we can define an interceptor function which does this decomposition before falling back to the default."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0sJ1uY6X7l3t",
        "colab_type": "code",
        "outputId": "19e4222a-d4f2-4685-ef6f-d2107ba2ba3f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "def my_decompose(op):\n",
        "    if isinstance(op, cirq.GateOperation) and isinstance(op.gate, HXGate):\n",
        "        return cirq.Z(*op.qubits), cirq.H(*op.qubits)\n",
        "\n",
        "cirq.Circuit.from_ops(cirq.decompose(HX(a), intercepting_decomposer=my_decompose))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<pre style=\"overflow: auto; white-space: pre;\">a: ───Z───Y^0.5───X───</pre>"
            ],
            "text/plain": [
              "a: ───Z───Y^0.5───X───"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 35
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OChSQ05Z06mL",
        "colab_type": "text"
      },
      "source": [
        "You can also define a predicate that says which gates to keep without decomposing further.\n",
        "The default predicate is to only keep gates that cannot be decomposed."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "KQ2in0ol05S9",
        "colab_type": "code",
        "outputId": "da3ecf3f-751f-4ba0-8c44-bc2904a4ef58",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "def keep_h_and_x(op):\n",
        "    return isinstance(op, cirq.GateOperation) and op.gate in [cirq.H, cirq.X]\n",
        "\n",
        "print(cirq.decompose(HX(a), keep=keep_h_and_x))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[cirq.H.on(cirq.NamedQubit('a')), cirq.X.on(cirq.NamedQubit('a'))]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3HtlMxa6QpVo",
        "colab_type": "text"
      },
      "source": [
        "## Parameterized Circuits\n",
        "\n",
        "In addition to circuit gates with fixed values, Cirq also supports parameterized gates with symbolic values via `sympy`. These are placeholder values, such as `sympy.Symbol('x')`, that will only be resolved at *run-time*. For simulators these values are resolved by providing a `ParamResolver`.  A `ParamResolver` provides a map from the `Symbol`'s name to its assigned value.  Plain python dictionaries can be used whenever a `ParamResolver` is needed."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "TIaVRzCD4deU",
        "colab_type": "code",
        "outputId": "f21183a6-dfff-44b3-d771-21e93f9b6636",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 195
        }
      },
      "source": [
        "import sympy as sp\n",
        "\n",
        "a = cirq.NamedQubit('a')\n",
        "b = cirq.NamedQubit('b')\n",
        "simulator = cirq.Simulator()\n",
        "\n",
        "val = sp.Symbol('s')\n",
        "pow_x_gate = cirq.X**val\n",
        "circuit = cirq.Circuit()\n",
        "circuit.append([pow_x_gate(a), pow_x_gate(b)])\n",
        "print('Circuit with parameterized gates:')\n",
        "print(circuit)\n",
        "print()\n",
        "for y in range(5):\n",
        "    result = simulator.simulate(circuit, param_resolver={'s': y / 4.0})\n",
        "    print('s={}: {}'.format(y, np.around(result.final_state, 2)))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Circuit with parameterized gates:\n",
            "a: ───X^s───\n",
            "\n",
            "b: ───X^s───\n",
            "\n",
            "s=0: [1.+0.j 0.+0.j 0.+0.j 0.+0.j]\n",
            "s=1: [ 0.6 +0.6j   0.25-0.25j  0.25-0.25j -0.1 -0.1j ]\n",
            "s=2: [0. +0.5j 0.5+0.j  0.5+0.j  0. -0.5j]\n",
            "s=3: [-0.1 +0.1j   0.25+0.25j  0.25+0.25j  0.6 -0.6j ]\n",
            "s=4: [0.+0.j 0.+0.j 0.+0.j 1.+0.j]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1_2i73Oo4aM1",
        "colab_type": "text"
      },
      "source": [
        "Here we see that the `Symbol` is used in two gates, and then the resolver provides this value at run time.\n",
        "\n",
        "Parameterized values are most useful in defining what we call a `Study`.  A `Study` is a collection of trials, where each  trial is a run with a particular set of configurations and which may be run repeatedly.  Running a study returns a list of `TrialResult`s per set of fixed parameter values and repetitions.  Example:\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Gj_Y3Lrh49o9",
        "colab_type": "code",
        "outputId": "239e3ead-4acf-408b-fca2-762e0407e07e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 497
        }
      },
      "source": [
        "resolvers = [cirq.ParamResolver({'s': y / 8.0}) for y in range(9)]\n",
        "circuit = cirq.Circuit()\n",
        "circuit.append([pow_x_gate(a), pow_x_gate(b)])\n",
        "circuit.append([cirq.measure(a), cirq.measure(b)])\n",
        "results = simulator.run_sweep(program=circuit,\n",
        "                              params=resolvers,\n",
        "                              repetitions=10)\n",
        "for i, result in enumerate(results):\n",
        "    print('params: {}\\n{}'.format(result.params.param_dict, result))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "params: {'s': 0.0}\n",
            "a=0000000000\n",
            "b=0000000000\n",
            "params: {'s': 0.125}\n",
            "a=0000000010\n",
            "b=0001000000\n",
            "params: {'s': 0.25}\n",
            "a=0100000000\n",
            "b=1100000000\n",
            "params: {'s': 0.375}\n",
            "a=0000000001\n",
            "b=0000001010\n",
            "params: {'s': 0.5}\n",
            "a=0110111010\n",
            "b=0110010100\n",
            "params: {'s': 0.625}\n",
            "a=1110111110\n",
            "b=1011011110\n",
            "params: {'s': 0.75}\n",
            "a=1110111101\n",
            "b=1111011111\n",
            "params: {'s': 0.875}\n",
            "a=1111111111\n",
            "b=1111111111\n",
            "params: {'s': 1.0}\n",
            "a=1111111111\n",
            "b=1111111111\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "av09E7pH44YO",
        "colab_type": "text"
      },
      "source": [
        "Above we passed in a list of `ParamResolver`s to the `params` parameter of `run_sweep`.  But one can also pass in a `Sweepable`.  There are some useful methods for generating `Sweepable`s, for example to generate an equally spaced set of param resolvers one can use `Linspace`\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zOymGxlb72Fk",
        "colab_type": "code",
        "outputId": "27f35607-79e8-4120-ec34-2a28371c6ba9",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 213
        }
      },
      "source": [
        "linspace = cirq.Linspace(start=0, stop=1.0, length=11, key='x')\n",
        "for p in linspace:\n",
        "    print(p)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "cirq.ParamResolver(OrderedDict([('x', 0.0)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.1)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.2)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.3)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.4)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.5)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.6)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.7)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.8)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 0.9)]))\n",
            "cirq.ParamResolver(OrderedDict([('x', 1.0)]))\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r-CjbPwkRI_I",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Rotate a qubit\n",
        "\n",
        "Let's do the equivalent of a Rabi-flop experiment.  That is lets apply an `PowXGate` rotating about the `X` axis for a linearly spaced set of values followed by a computational basis measurement.  The end result should be a plot of the sampled fraction that were $|1\\rangle$ as a function of gates of $X^t$ for $t$ between 0 and $1$ for 100 values of $t$ and each result sampled 100 times.\n",
        "\n",
        "You can use matplotlib to plot this, see example plot code below."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "F63NaIkg84ga",
        "colab_type": "code",
        "outputId": "01b4e4fd-8e24-48f3-c6c7-e4836d77283f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 280
        }
      },
      "source": [
        "import matplotlib.pyplot as plt\n",
        "plt.plot([1, 2, 3], [5, 3, 10])"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x7f73f1cb3e80>]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 39
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "8yW2e3sq9JM8",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Insert your code here"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FEM73JPALREa",
        "colab_type": "text"
      },
      "source": [
        "## Noise\n",
        "\n",
        "In addition to circuits with unitary gates, Cirq also has support for modeling noisy quantum evolutions. This is useful when modeling what will happen when running on actual hardware.\n",
        "\n",
        "Cirq currently supports noise that fits within the context of *operator sum representations* of noise (a.k.a quantum operations, quantum dyanamical maps, superoperators, etc).  This formalism models the evolution of a density matrix via \n",
        "$$\\rho \\rightarrow \\sum_k A_k \\rho A_k^\\dagger$$\n",
        "where the $A_k$ are *Krauss operators*.  These operators are not necessarily unitary and satisfy the property\n",
        "$$ \\sum_k A_k^\\dagger A_k = I$$\n",
        "\n",
        "An example of a noise operator is the depolarizing channel on one qubit.  This takes\n",
        "$$\\rho \\rightarrow (1-p) \\rho + \\frac{p}{3} (X \\rho X + Y \\rho Y + Z \\rho Z)$$\n",
        "In Cirq we can define such a channel and use it in a quantum circuit:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YclVFbKZ0aD4",
        "colab_type": "code",
        "outputId": "9218bc01-eaf1-4b06-885a-2145b8fb0444",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops(cirq.depolarize(0.2)(a), cirq.measure(a))\n",
        "print(circuit)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───D(0.2)───M───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CvteYd9s00t_",
        "colab_type": "text"
      },
      "source": [
        "Previously we saw that gates could implement that `_unitary_` protocol, and by doing so they could be used to perform wave function simulation.  For noise the gates implement the `_channel_` protocol.  Classes that implement this protocol return the Krauss operators on their `_channel_` method.  Thus"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0ig_NSrS12PE",
        "colab_type": "code",
        "outputId": "7c2519ee-5121-4adc-d9e6-0386576c931b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 248
        }
      },
      "source": [
        "for i, krauss in enumerate(cirq.channel(cirq.depolarize(0.2))):\n",
        "    print('{}th krauss operator is {}'.format(i, krauss))\n",
        "print()\n",
        "\n",
        "for i, krauss in enumerate(cirq.channel(cirq.depolarize(0.2))):\n",
        "    pauli_ex = cirq.expand_matrix_in_orthogonal_basis(krauss, cirq.PAULI_BASIS)\n",
        "    print('{}th krauss operator is {}'.format(i, pauli_ex))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0th krauss operator is [[0.89442719 0.        ]\n",
            " [0.         0.89442719]]\n",
            "1th krauss operator is [[0.        +0.j 0.25819889+0.j]\n",
            " [0.25819889+0.j 0.        +0.j]]\n",
            "2th krauss operator is [[0.+0.j         0.-0.25819889j]\n",
            " [0.+0.25819889j 0.+0.j        ]]\n",
            "3th krauss operator is [[ 0.25819889+0.j  0.        +0.j]\n",
            " [ 0.        +0.j -0.25819889+0.j]]\n",
            "\n",
            "0th krauss operator is 0.8944271909999159*I\n",
            "1th krauss operator is 0.2581988897471611*X\n",
            "2th krauss operator is 0.2581988897471611*Y\n",
            "3th krauss operator is 0.2581988897471611*Z\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "spU2l26r0tQS",
        "colab_type": "text"
      },
      "source": [
        "In addition to the wave function simulator, Cirq also has a density matrix simulator.  Instead of keeping track of the wave function this simulator keeps track of the density matrix.  It has the same `run` and `simulate` type methods.  For example we can use this to simulate depolarizing channel and return the final density matrix of the system"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "skLIvXYq4yvX",
        "colab_type": "code",
        "outputId": "530083fb-66b6-4f8d-8343-3fbbad2bed1d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 124
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops(cirq.depolarize(0.2)(a))\n",
        "print('Circuit:\\n{}\\n'.format(circuit))\n",
        "simulator = cirq.DensityMatrixSimulator()\n",
        "matrix = simulator.simulate(circuit).final_density_matrix\n",
        "print('Final density matrix:\\n{}'.format(matrix))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Circuit:\n",
            "a: ───D(0.2)───\n",
            "\n",
            "Final density matrix:\n",
            "[[0.8666666 +0.j 0.        +0.j]\n",
            " [0.        +0.j 0.13333333+0.j]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Eudp7NYo51LT",
        "colab_type": "text"
      },
      "source": [
        "One thing to note is that the density matrix simulator simulates measurement statistically, and not as a channel where the outcome is not known.  Thus for example "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_SjPRrIX5F4O",
        "colab_type": "code",
        "outputId": "fe015980-fbd6-4f86-f99a-993e66fcb57b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 195
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops(cirq.depolarize(0.2)(a), cirq.measure(a))\n",
        "simulator = cirq.DensityMatrixSimulator()\n",
        "for _ in range(5):\n",
        "    print(simulator.simulate(circuit).final_density_matrix)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[0.       +0.j 0.       +0.j]\n",
            " [0.       +0.j 0.9999999+0.j]]\n",
            "[[0.       +0.j 0.       +0.j]\n",
            " [0.       +0.j 0.9999999+0.j]]\n",
            "[[0.99999994+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.        +0.j]]\n",
            "[[0.99999994+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.        +0.j]]\n",
            "[[0.99999994+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.        +0.j]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3qUM-Kpi8Iy4",
        "colab_type": "text"
      },
      "source": [
        "### Monte carlo simulations\n",
        "\n",
        "Density matrix simulations are more expensive than pure state wave function simulations.  However some channels allow an interpreation of randomly applying one of a fixed set of unitaries with differing probabilites.  For example the depolarizing channel above can be interpretted as: with probability $1-p$ apply the identity to the state and with probability $p$ apply one of the three Pauli matrices $X$, $Y$, or $Z$ with equal probability.  Channels that can be interpretted in this form can be simulating using a wave function simulator: when this channel is simulated the simulation will sample a unitary with the appropriate probability.  \n",
        "\n",
        "For channels of these type, the channel can, instead of implementing the `_channel_` protocol, implement the `_mixture_` protocol:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9Pt7o-Tq2SNz",
        "colab_type": "code",
        "outputId": "16c898db-bbcb-49cb-8594-0adbd2c0943b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 373
        }
      },
      "source": [
        "for p, u in cirq.mixture(cirq.depolarize(0.2)):\n",
        "    print('prob={}\\nunitary\\n{}\\n'.format(p, u))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "prob=0.8\n",
            "unitary\n",
            "[[1. 0.]\n",
            " [0. 1.]]\n",
            "\n",
            "prob=0.06666666666666667\n",
            "unitary\n",
            "[[0.+0.j 1.+0.j]\n",
            " [1.+0.j 0.+0.j]]\n",
            "\n",
            "prob=0.06666666666666667\n",
            "unitary\n",
            "[[0.+0.j 0.-1.j]\n",
            " [0.+1.j 0.+0.j]]\n",
            "\n",
            "prob=0.06666666666666667\n",
            "unitary\n",
            "[[ 1.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j]]\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z4FIsadc4Nmg",
        "colab_type": "text"
      },
      "source": [
        "In fact the depolarizing channel does not implement `_channel_`.  Instead it only implements `_mixture_` and the `cirq.channel` method notices this and derives the channel from the mixture."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HvhpBD334o1v",
        "colab_type": "code",
        "outputId": "274137c8-9947-428c-8cc0-c39f2d81d0f7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        }
      },
      "source": [
        "d = cirq.depolarize(0.2)\n",
        "print('does cirq.depolarize(0.2) have _channel_? {}'.format('yes' if getattr(d, '_channel_', None) else 'no'))\n",
        "print('does cirq.depolarize(0.2) have _mixture_? {}'.format('yes' if getattr(d, '_mixture_', None) else 'no'))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "does cirq.depolarize(0.2) have _channel_? no\n",
            "does cirq.depolarize(0.2) have _mixture_? yes\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OMCyapOd28h2",
        "colab_type": "text"
      },
      "source": [
        "When channels implement mixture then, as we said, we can use the wave function simulator:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vDEhGG0v-UJy",
        "colab_type": "code",
        "outputId": "a526bad0-3f8f-4260-db16-1bc5c7b47659",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops(cirq.depolarize(0.5).on(a), cirq.measure(a))\n",
        "simulator = cirq.Simulator()\n",
        "result = simulator.run(circuit, repetitions=10)\n",
        "print(result)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a=0100010000\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "weWPzaPx8zly",
        "colab_type": "text"
      },
      "source": [
        "### Adding Noise to Circuits and Simulations"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X-dT_CLu0fH9",
        "colab_type": "text"
      },
      "source": [
        "To add noise to circuits or during simulations, we provide the notion of a `NoiseModel`.  A `NoiseModel` may add noise operation by operation, or it may add noise moment by moment, or it may add noise across a list of moments.\n",
        "\n",
        "For example we can define a noise model that add a single qubit depolarizing for every qubit in each moment.  "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "PfRP7K598wNQ",
        "colab_type": "code",
        "outputId": "c51d2c2f-f338-42aa-c87d-d40adf2342c1",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 170
        }
      },
      "source": [
        "noise = cirq.ConstantQubitNoiseModel(cirq.depolarize(0.2))\n",
        "circuit = cirq.Circuit.from_ops(cirq.H(a), cirq.CNOT(a, b), cirq.measure(a, b))\n",
        "print('Circuit with no noise:\\n{}\\n'.format(circuit))\n",
        "\n",
        "system_qubits = sorted(circuit.all_qubits())\n",
        "noisy_circuit = cirq.Circuit()\n",
        "for moment in circuit:\n",
        "    noisy_circuit.append(noise.noisy_moment(moment, system_qubits))\n",
        "print('Circuit with noise:\\n{}'.format(noisy_circuit))\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Circuit with no noise:\n",
            "a: ───H───@───M───\n",
            "          │   │\n",
            "b: ───────X───M───\n",
            "\n",
            "Circuit with noise:\n",
            "a: ───H────────D(0.2)───@───D(0.2)───M───D(0.2)───\n",
            "                        │            │\n",
            "b: ───D(0.2)────────────X───D(0.2)───M───D(0.2)───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uzxaFCGIz2aQ",
        "colab_type": "code",
        "outputId": "2e4dc8dd-d146-48af-ea2c-75b6d49d653c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 323
        }
      },
      "source": [
        "noise = cirq.ConstantQubitNoiseModel(cirq.depolarize(0.2))\n",
        "circuit = cirq.Circuit.from_ops(cirq.H(a), cirq.CNOT(a, b), cirq.measure(a, b))\n",
        "\n",
        "simulator = cirq.DensityMatrixSimulator(noise=noise)\n",
        "for i, step in enumerate(simulator.simulate_moment_steps(circuit)):\n",
        "    print('After step {} state was\\n{}\\n'.format(i, step.density_matrix()))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "After step 0 state was\n",
            "[[0.4333333 +0.j 0.        +0.j 0.31777775+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.06666666+0.j 0.        +0.j 0.04888888+0.j]\n",
            " [0.31777775+0.j 0.        +0.j 0.4333333 +0.j 0.        +0.j]\n",
            " [0.        +0.j 0.04888888+0.j 0.        +0.j 0.06666666+0.j]]\n",
            "\n",
            "After step 1 state was\n",
            "[[0.34859255+0.j 0.        +0.j 0.        +0.j 0.17089382+0.j]\n",
            " [0.        +0.j 0.15140739+0.j 0.02629136+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.02629136+0.j 0.15140739+0.j 0.        +0.j]\n",
            " [0.17089382+0.j 0.        +0.j 0.        +0.j 0.34859255+0.j]]\n",
            "\n",
            "After step 2 state was\n",
            "[[0.11555554+0.j 0.        +0.j 0.        +0.j 0.        +0.j]\n",
            " [0.        +0.j 0.75111103+0.j 0.        +0.j 0.        +0.j]\n",
            " [0.        +0.j 0.        +0.j 0.01777777+0.j 0.        +0.j]\n",
            " [0.        +0.j 0.        +0.j 0.        +0.j 0.11555553+0.j]]\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PvJCA3e0QsuI",
        "colab_type": "text"
      },
      "source": [
        "## Devices\n",
        "\n",
        "NISQ algorithms work in a regime where every gate counts.  A key philosophy behind Cirq is that we believe the details of the hardware, the performance characteristics, as well as device constraints, will be key to getting the most out of NISQ algorithms.  Towards this end these hardware features are contained in the `Device` class.\n",
        "\n",
        "For example, here is Google's Bristleconde device"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BmzxGpDB9jJ4",
        "colab_type": "code",
        "outputId": "edf50bbf-1bcc-4816-ed53-3959afa3c4ac",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 544
        }
      },
      "source": [
        "print(cirq.google.Bristlecone)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "                                             (0, 5)────(0, 6)\n",
            "                                             │         │\n",
            "                                             │         │\n",
            "                                    (1, 4)───(1, 5)────(1, 6)────(1, 7)\n",
            "                                    │        │         │         │\n",
            "                                    │        │         │         │\n",
            "                           (2, 3)───(2, 4)───(2, 5)────(2, 6)────(2, 7)───(2, 8)\n",
            "                           │        │        │         │         │        │\n",
            "                           │        │        │         │         │        │\n",
            "                  (3, 2)───(3, 3)───(3, 4)───(3, 5)────(3, 6)────(3, 7)───(3, 8)───(3, 9)\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  │        │        │        │         │         │        │        │\n",
            "         (4, 1)───(4, 2)───(4, 3)───(4, 4)───(4, 5)────(4, 6)────(4, 7)───(4, 8)───(4, 9)───(4, 10)\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "(5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4)───(5, 5)────(5, 6)────(5, 7)───(5, 8)───(5, 9)───(5, 10)───(5, 11)\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         │        │        │        │        │         │         │        │        │        │\n",
            "         (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5)────(6, 6)────(6, 7)───(6, 8)───(6, 9)───(6, 10)\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  │        │        │        │         │         │        │        │\n",
            "                  (7, 2)───(7, 3)───(7, 4)───(7, 5)────(7, 6)────(7, 7)───(7, 8)───(7, 9)\n",
            "                           │        │        │         │         │        │\n",
            "                           │        │        │         │         │        │\n",
            "                           (8, 3)───(8, 4)───(8, 5)────(8, 6)────(8, 7)───(8, 8)\n",
            "                                    │        │         │         │\n",
            "                                    │        │         │         │\n",
            "                                    (9, 4)───(9, 5)────(9, 6)────(9, 7)\n",
            "                                             │         │\n",
            "                                             │         │\n",
            "                                             (10, 5)───(10, 6)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GGejgRf0AGni",
        "colab_type": "text"
      },
      "source": [
        "In a future version, we intend for each `Device` to define a noise model.\n",
        "\n",
        "`Device`s also contain more information about the timing of the device.  For example here we can calculate the duration of an `Exp11Gate` on the `Bristlecone` device"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HAwdWkprAPXN",
        "colab_type": "code",
        "outputId": "af744f29-b3ab-4d9b-a2a7-2bd7f435c68b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "brissy = cirq.google.Bristlecone\n",
        "op = cirq.X.on(cirq.GridQubit(5, 5))\n",
        "print(brissy.duration_of(op))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "20ns\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IxnyURhnAdph",
        "colab_type": "text"
      },
      "source": [
        "Another property of devices is that they can be used to enforce constraints from the hardware, both checking that these constraints are satisfied, but also enforcing the constraints on the device.  For example on the `Bristlecone` device a two qubit gate has the property that one cannot simultaneously perform a pair of two qubit gates that act on adjacent qubits. So for example if we create such a `Circuit` and validate it using the device it will yell at us"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "r5F4FUtmA5kW",
        "colab_type": "code",
        "outputId": "5e3fe3c9-e0d3-4bed-e8f9-24cabe0b17cf",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 494
        }
      },
      "source": [
        "q55 = cirq.GridQubit(5, 5)\n",
        "q56 = cirq.GridQubit(5, 6)\n",
        "q66 = cirq.GridQubit(6, 6)\n",
        "q67 = cirq.GridQubit(6, 7)\n",
        "ops = [cirq.CZ(q55, q56), cirq.CZ(q66, q67)]\n",
        "circuit = cirq.Circuit.from_ops(ops)\n",
        "print(circuit)\n",
        "print('But when we validate it against the device:')\n",
        "cirq.google.Bristlecone.validate_circuit(circuit)\n",
        "# (this should throw an error)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "(5, 5): ───@───\n",
            "           │\n",
            "(5, 6): ───@───\n",
            "\n",
            "(6, 6): ───@───\n",
            "           │\n",
            "(6, 7): ───@───\n",
            "But when we validate it against the device:\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "error",
          "ename": "ValueError",
          "evalue": "ignored",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
            "\u001b[0;32m<ipython-input-49-28d48e3edbcf>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'But when we validate it against the device:'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mcirq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgoogle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mBristlecone\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_circuit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m \u001b[0;31m# (this should throw an error)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/cirq/google/xmon_device.py\u001b[0m in \u001b[0;36mvalidate_circuit\u001b[0;34m(self, circuit)\u001b[0m\n\u001b[1;32m    146\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    147\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mvalidate_circuit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mcircuits\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCircuit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 148\u001b[0;31m         \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_circuit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    149\u001b[0m         \u001b[0m_verify_unique_measurement_keys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mall_operations\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    150\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/cirq/devices/device.py\u001b[0m in \u001b[0;36mvalidate_circuit\u001b[0;34m(self, circuit)\u001b[0m\n\u001b[1;32m     82\u001b[0m         \"\"\"\n\u001b[1;32m     83\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mmoment\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_moment\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmoment\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     86\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mvalidate_moment\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmoment\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'cirq.Moment'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/cirq/google/xmon_device.py\u001b[0m in \u001b[0;36mvalidate_moment\u001b[0;34m(self, moment)\u001b[0m\n\u001b[1;32m    160\u001b[0m                                 cast(ops.GateOperation, other))):\n\u001b[1;32m    161\u001b[0m                         raise ValueError(\n\u001b[0;32m--> 162\u001b[0;31m                             'Adjacent Exp11 operations: {}.'.format(moment))\n\u001b[0m\u001b[1;32m    163\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    164\u001b[0m     def can_add_operation_into_moment(self,\n",
            "\u001b[0;31mValueError\u001b[0m: Adjacent Exp11 operations: CZ((5, 5), (5, 6)) and CZ((6, 6), (6, 7))."
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XkNPbeFDBTL4",
        "colab_type": "text"
      },
      "source": [
        "But more interestingly we could have passed the device into the `Circuit` and it will perform the creation of the circuit (using the insertion semanics as described above) such that the device cannot violate the constraints."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "5BOBUIEIBeQ5",
        "colab_type": "code",
        "outputId": "77abccff-b2f8-49bd-9dbd-91804583862f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 136
        }
      },
      "source": [
        "ops = [cirq.CZ(q55, q56), cirq.CZ(q66, q67)]\n",
        "circuit = cirq.Circuit(device=cirq.google.Bristlecone)\n",
        "circuit.append(ops)\n",
        "print(circuit)\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "(5, 5): ───@───────\n",
            "           │\n",
            "(5, 6): ───@───────\n",
            "\n",
            "(6, 6): ───────@───\n",
            "               │\n",
            "(6, 7): ───────@───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lyFcloaARXg4",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Make a Device\n",
        "\n",
        "Construct a device that acts on a square sized lattice, and only allows Hadamard, CZ, and measurement gates."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zDE-19I_a3on",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# your code here"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J9ia4eatUQ_x",
        "colab_type": "text"
      },
      "source": [
        "## Compiling / Optimizing\n",
        "\n",
        "Cirq's philosophy for what are often called compilation steps is slightly non-traditional.  In particular we believe that NISQ devices perform so few gates that many compiliations will be one or a few passes. A useful class of optimization codee for this is the `PointOptimizer`. For example here is a `PointOptimizer` that recognizes that a `X` gate followed by a `Z` gate is equivalent to a `Y` gate"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "l7eFMVe1GEe2",
        "colab_type": "code",
        "outputId": "c6eea3f6-b16c-4ada-e424-ee4d18ad4ca3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 170
        }
      },
      "source": [
        "class XZOptimizer(cirq.PointOptimizer):\n",
        "    \"\"\"Replaces an X followed by a Z with a Y.\"\"\"\n",
        "    \n",
        "    def optimization_at(self, circuit, index, op):\n",
        "        # Is the gate an X gate?\n",
        "        if isinstance(op, cirq.GateOperation) and (op.gate == cirq.X):\n",
        "            next_op_index = circuit.next_moment_operating_on(op.qubits, index + 1)\n",
        "            qubit = op.qubits[0]\n",
        "            if next_op_index is not None:\n",
        "                next_op = circuit.operation_at(qubit, next_op_index)\n",
        "                if isinstance(next_op, cirq.GateOperation) and  (next_op.gate == cirq.Z):\n",
        "                    new_op = cirq.Y.on(qubit)\n",
        "                    return cirq.PointOptimizationSummary(\n",
        "                        clear_span = next_op_index - index + 1,\n",
        "                        clear_qubits=op.qubits, \n",
        "                        new_operations=[new_op])\n",
        "        \n",
        "opt = XZOptimizer()\n",
        "circuit = cirq.Circuit.from_ops(cirq.X(a), cirq.Z(a), cirq.CZ(a, b), cirq.X(a))\n",
        "print('Before\\n{}\\n'. format(circuit))\n",
        "opt.optimize_circuit(circuit)\n",
        "print('After\\n{}'.format(circuit))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Before\n",
            "a: ───X───Z───@───X───\n",
            "              │\n",
            "b: ───────────@───────\n",
            "\n",
            "After\n",
            "a: ───Y───────@───X───\n",
            "              │\n",
            "b: ───────────@───────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "--aUfkiaUb3S",
        "colab_type": "text"
      },
      "source": [
        "### Exercise: Simplify flipped CNOTs\n",
        "\n",
        "Write an PointOptimizer that performs (greedily) the simplification that\n",
        "\n",
        "```\n",
        "a: ───H───@───H───\n",
        "          │\n",
        "b: ───H───X───H───\n",
        "```\n",
        "is equal to\n",
        "\n",
        "\n",
        "```\n",
        "a: ───X───\n",
        "      │\n",
        "b: ───@───\n",
        "```\n",
        "\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "S0PThmctKFxl",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Insert your code here."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "75D9xMroKNDG",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Here is a circuit to test this on.\n",
        "circuit = cirq.Circuit.from_ops(cirq.H(a), cirq.H(a), cirq.H(b), \n",
        "                                cirq.CNOT(a, b), cirq.H(a), cirq.H(b), \n",
        "                                cirq.CZ(a, b))\n",
        "# Instantiate your optimizer\n",
        "\n",
        "# And check that it worked.\n",
        "print(my_opt.optimizer_circuit(circuit))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gM1ZW7TstvK9",
        "colab_type": "text"
      },
      "source": [
        "## Google's Xmon Gates\n",
        "\n",
        "We built Cirq at Google because we thought that it was important that hardware details flow up through the level of abstractions in a framework for NISQ algorithms.  And we also built it because Google has a class of superconducting qubit devices where we want to explore NISQ algorithms.  \n",
        "\n",
        "What sort of gates does the xmon architecture support?  At the hardware level the basic gate set is rotations about the Pauli $Z$ axis\n",
        "$$\\exp(-iZt) = \\left[ \n",
        "\\begin{array}\n",
        "e^{it} & 0 \\\\\n",
        "0 & e^{-it}\n",
        "\\end{array}\n",
        "\\right]$$\n",
        "Rotations about any axis in the Pauli $X$ and $Y$ plane:\n",
        "$$\\exp(-i(\\cos \\theta X + \\sin \\theta Y) t) = \n",
        "\\left[\n",
        "\\begin{array}\n",
        "\\cos t & -i\\sin t e^{-i \\theta} \\\\\n",
        "-i\\sin t e^{i \\theta} & \\cos t\n",
        "\\end{array}\n",
        "\\right]\n",
        "$$\n",
        "The two qubit gate is a phase about the $|11\\rangle$ state:\n",
        "$$\n",
        "\\exp(-it |11\\rangle \\langle 11|) =\n",
        "\\left[ \n",
        "\\begin{array}\n",
        "1 & 0 & 0 & 0 \\\\\n",
        "0 & 1 & 0 & 0 \\\\\n",
        "0 & 0 & 1 & 0 \\\\\n",
        "0 & 0 & 0 & \\exp(-it)\n",
        "\\end{array}\n",
        "\\right]\n",
        "$$\n",
        "And finally a measurement in the computational basis.\n",
        "\n",
        "Many of the common gates in Cirq are directly accessible on this hardware.  To check whether the gate you are using is in the xmon gate set, one can use `is_native_xmon_op`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-SqOf-yt2XL3",
        "colab_type": "code",
        "outputId": "5874c96d-adf6-400a-e487-9addfe9b3475",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "cirq.google.is_native_xmon_op(cirq.X.on(cirq.NamedQubit('a')))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 51
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "yutgDgR72aUi",
        "colab_type": "code",
        "outputId": "fe59939f-9118-4074-aebe-38849ef8d9c4",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "cirq.google.is_native_xmon_op(cirq.CNOT.on(cirq.NamedQubit('a'), cirq.NamedQubit('b')))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "False"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 52
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4FEVSq1T2n2J",
        "colab_type": "text"
      },
      "source": [
        "When a gate is not a native xmon gate, Cirq can often convert it to an xmon native gate;"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AdqDGjqL2lI2",
        "colab_type": "code",
        "outputId": "3343a271-3d0f-4fe2-fe8f-303de7fa26b8",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "converter = cirq.google.ConvertToXmonGates()\n",
        "converted = converter.convert(cirq.CNOT.on(cirq.NamedQubit('a'), cirq.NamedQubit('b')))\n",
        "print(cirq.Circuit.from_ops(converted))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───Z^0.75────X^0.5────S^-1───Y^-0.5───@───S^-1───Y^0.5───Y^0.5─────Z^-0.75───\n",
            "                                         │\n",
            "b: ───X^-0.25───Y^-0.5───────────────────@───S^-1───Y^0.5───X^-0.25─────────────\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iR8zA_uY4HRZ",
        "colab_type": "text"
      },
      "source": [
        "This isn't very optimized because what it has done is first rely on the decompose of `CNOT` and then decomposed each of these into native xmon gates.  There are many tricks that one can use to simplify xmon gates.  To apply many of these one can use the `cirq.google.optimize_from_xmon` method:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ePc0hrEU2_yy",
        "colab_type": "code",
        "outputId": "34274816-f7b7-4c7b-847d-eed671396a94",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "circuit = cirq.Circuit.from_ops([cirq.CNOT.on(cirq.NamedQubit('a'), cirq.NamedQubit('b'))])\n",
        "print(cirq.google.optimized_for_xmon(circuit))\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a: ───────────────────────@──────────────────────\n",
            "                          │\n",
            "b: ───PhasedX(-0.5)^0.5───@───PhasedX(0.5)^0.5───\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lImnJ5Co4Y1n",
        "colab_type": "text"
      },
      "source": [
        "Because xmon gates can be executed on Google hardware, they will need to be transmitted as machine code to the quantum computer.  This means that they have a serialized form.  We use protobuffers as the serialization.  To see what this form looks like use `cirq.google.gate_to_proto_dict`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "od6ofvow4EoD",
        "colab_type": "code",
        "outputId": "00b2ffad-e60e-4388-cdf1-9b38dd435a59",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 68
        }
      },
      "source": [
        "cirq.google.gate_to_proto_dict(cirq.X, [cirq.GridQubit(5, 5)])"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'exp_w': {'axis_half_turns': {'raw': 0.0},\n",
              "  'half_turns': {'raw': 1.0},\n",
              "  'target': {'col': 5, 'row': 5}}}"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 115
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uXjS2ro6JL6Y",
        "colab_type": "text"
      },
      "source": [
        "[[[This is where we cut over to demo of running a quantum program via Quantum Engine.]]]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tMC-nLE474nH",
        "colab_type": "text"
      },
      "source": [
        "# Other interesting things in Cirq\n",
        "\n",
        "**Experiments**. The `cirq.experiments` package can perform and plot the results of some basic experiments for understanding how well a system is performing.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ih8YgwX19h2-",
        "colab_type": "code",
        "outputId": "10c06c26-b9e4-4612-aa5d-e8e56bc40778",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 283
        }
      },
      "source": [
        "result = cirq.experiments.rabi_oscillations(\n",
        "    sampler=cirq.Simulator(),  # In the future, sampler could point at real hardware.\n",
        "    qubit=cirq.LineQubit(0)\n",
        ")\n",
        "result.plot()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nAAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "38c8tWcX90Zn",
        "colab_type": "text"
      },
      "source": [
        "**Testing**. The `cirq.testing` package has useful debugging and testing methods like `cirq.testing.assert_implements_consistent_protocols` and `cirq.testing.assert_allclose_up_to_global_phase`.\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "j7FoZGKv90qe",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class InconsistentXGate(cirq.SingleQubitGate):\n",
        "\n",
        "    def _decompose_(self, qubits):\n",
        "        yield cirq.H(qubits[0])\n",
        "        yield cirq.Z(qubits[0])\n",
        "        yield cirq.H(qubits[0])\n",
        "\n",
        "    def _unitary_(self):\n",
        "        return np.array([[0, -1j], [1j, 0]])  # Oops! Y instead of X!\n",
        "\n",
        "cirq.testing.assert_decompose_is_consistent_with_unitary(InconsistentXGate())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iYqy0qwU9sZy",
        "colab_type": "text"
      },
      "source": [
        "**Export**. You can export a circuit as QASM."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qH7xB-vZ-Jsn",
        "colab_type": "code",
        "outputId": "1d8b453c-43ad-492e-ed21-f92f556e0ce5",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 306
        }
      },
      "source": [
        "a, b, c = cirq.LineQubit.range(3)\n",
        "circuit = cirq.Circuit.from_ops(cirq.H(a), cirq.H(c), cirq.CNOT(a, b), cirq.CCZ(a, b, c))\n",
        "print(circuit.to_qasm())"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "// Generated from Cirq v0.5.0\n",
            "\n",
            "OPENQASM 2.0;\n",
            "include \"qelib1.inc\";\n",
            "\n",
            "\n",
            "// Qubits: [0, 1, 2]\n",
            "qreg q[3];\n",
            "\n",
            "\n",
            "h q[0];\n",
            "h q[2];\n",
            "cx q[0],q[1];\n",
            "h q[2];\n",
            "ccx q[0],q[1],q[2];\n",
            "h q[2];\n",
            "\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QlgFtRQs-TfQ",
        "colab_type": "text"
      },
      "source": [
        "You can also turn a circuit into a link to the drag-and-drop web simulation Quirk (though somewhat inconveniently)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ydst5b0S9IGE",
        "colab_type": "code",
        "outputId": "0d9c8a32-7deb-4703-9488-d9d3f280bf8f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "from cirq.contrib.quirk.export_to_quirk import circuit_to_quirk_url\n",
        "print(circuit_to_quirk_url(circuit))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "http://algassert.com/quirk#circuit=%7B%22cols%22%3A%5B%5B%22H%22%2C1%2C%22H%22%5D%2C%5B%22%E2%80%A2%22%2C%22X%22%5D%2C%5B%22%E2%80%A2%22%2C%22%E2%80%A2%22%2C%22Z%22%5D%5D%7D\n"
          ],
          "name": "stdout"
        }
      ]
    }
  ]
}